pulsesrc.c 37.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 *  GStreamer pulseaudio plugin
 *
 *  Copyright (c) 2004-2008 Lennart Poettering
 *
 *  gst-pulse is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation; either version 2.1 of the
 *  License, or (at your option) any later version.
 *
 *  gst-pulse 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with gst-pulse; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 *  USA.
 */

22 23 24 25
/**
 * SECTION:element-pulsesrc
 * @see_also: pulsesink, pulsemixer
 *
26 27 28
 * This element captures audio from a
 * <ulink href="http://www.pulseaudio.org">PulseAudio sound server</ulink>.
 *
29 30
 * <refsect2>
 * <title>Example pipelines</title>
31
 * |[
32
 * gst-launch -v pulsesrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=alsasrc.ogg
33
 * ]| Record from a sound card using pulseaudio and encode to Ogg/Vorbis.
34 35 36
 * </refsect2>
 */

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include <stdio.h>

#include <gst/base/gstbasesrc.h>
#include <gst/gsttaglist.h>

#include "pulsesrc.h"
#include "pulseutil.h"
#include "pulsemixerctrl.h"

GST_DEBUG_CATEGORY_EXTERN (pulse_debug);
#define GST_CAT_DEFAULT pulse_debug

54 55 56 57
#define DEFAULT_SERVER            NULL
#define DEFAULT_DEVICE            NULL
#define DEFAULT_DEVICE_NAME       NULL

58 59
enum
{
60 61
  PROP_0,
  PROP_SERVER,
62
  PROP_DEVICE,
63
  PROP_DEVICE_NAME,
Andy Wingo's avatar
Andy Wingo committed
64
  PROP_CLIENT,
65
  PROP_STREAM_PROPERTIES,
66
  PROP_SOURCE_OUTPUT_INDEX,
67
  PROP_LAST
68 69
};

70 71
static void gst_pulsesrc_destroy_stream (GstPulseSrc * pulsesrc);
static void gst_pulsesrc_destroy_context (GstPulseSrc * pulsesrc);
72

73
static void gst_pulsesrc_set_property (GObject * object, guint prop_id,
74
    const GValue * value, GParamSpec * pspec);
75
static void gst_pulsesrc_get_property (GObject * object, guint prop_id,
76
    GValue * value, GParamSpec * pspec);
77
static void gst_pulsesrc_finalize (GObject * object);
78

79
static gboolean gst_pulsesrc_open (GstAudioSrc * asrc);
80

81
static gboolean gst_pulsesrc_close (GstAudioSrc * asrc);
82

83
static gboolean gst_pulsesrc_prepare (GstAudioSrc * asrc,
84
    GstRingBufferSpec * spec);
85

86
static gboolean gst_pulsesrc_unprepare (GstAudioSrc * asrc);
87

88
static guint gst_pulsesrc_read (GstAudioSrc * asrc, gpointer data,
89
    guint length);
90
static guint gst_pulsesrc_delay (GstAudioSrc * asrc);
91

92 93
static void gst_pulsesrc_reset (GstAudioSrc * src);

94 95
static gboolean gst_pulsesrc_negotiate (GstBaseSrc * basesrc);

96
static GstStateChangeReturn gst_pulsesrc_change_state (GstElement *
97 98
    element, GstStateChange transition);

99 100
static void gst_pulsesrc_init_interfaces (GType type);

101 102 103 104 105 106
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
# define ENDIANNESS   "LITTLE_ENDIAN, BIG_ENDIAN"
#else
# define ENDIANNESS   "BIG_ENDIAN, LITTLE_ENDIAN"
#endif

107
GST_IMPLEMENT_PULSEMIXER_CTRL_METHODS (GstPulseSrc, gst_pulsesrc);
108
GST_IMPLEMENT_PULSEPROBE_METHODS (GstPulseSrc, gst_pulsesrc);
109 110
GST_BOILERPLATE_FULL (GstPulseSrc, gst_pulsesrc, GstAudioSrc,
    GST_TYPE_AUDIO_SRC, gst_pulsesrc_init_interfaces);
111

112 113
static gboolean
gst_pulsesrc_interface_supported (GstImplementsInterface *
114 115
    iface, GType interface_type)
{
116
  GstPulseSrc *this = GST_PULSESRC_CAST (iface);
117 118 119 120

  if (interface_type == GST_TYPE_MIXER && this->mixer)
    return TRUE;

121 122 123
  if (interface_type == GST_TYPE_PROPERTY_PROBE && this->probe)
    return TRUE;

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
  return FALSE;
}

static void
gst_pulsesrc_implements_interface_init (GstImplementsInterfaceClass * klass)
{
  klass->supported = gst_pulsesrc_interface_supported;
}

static void
gst_pulsesrc_init_interfaces (GType type)
{
  static const GInterfaceInfo implements_iface_info = {
    (GInterfaceInitFunc) gst_pulsesrc_implements_interface_init,
    NULL,
    NULL,
  };
  static const GInterfaceInfo mixer_iface_info = {
    (GInterfaceInitFunc) gst_pulsesrc_mixer_interface_init,
    NULL,
    NULL,
  };
146 147 148 149 150
  static const GInterfaceInfo probe_iface_info = {
    (GInterfaceInitFunc) gst_pulsesrc_property_probe_interface_init,
    NULL,
    NULL,
  };
151 152 153 154

  g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
      &implements_iface_info);
  g_type_add_interface_static (type, GST_TYPE_MIXER, &mixer_iface_info);
155 156
  g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
      &probe_iface_info);
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
}

static void
gst_pulsesrc_base_init (gpointer g_class)
{

  static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("src",
      GST_PAD_SRC,
      GST_PAD_ALWAYS,
      GST_STATIC_CAPS ("audio/x-raw-int, "
          "endianness = (int) { " ENDIANNESS " }, "
          "signed = (boolean) TRUE, "
          "width = (int) 16, "
          "depth = (int) 16, "
          "rate = (int) [ 1, MAX ], "
172 173
          "channels = (int) [ 1, 32 ];"
          "audio/x-raw-float, "
174 175 176
          "endianness = (int) { " ENDIANNESS " }, "
          "width = (int) 32, "
          "rate = (int) [ 1, MAX ], "
177 178
          "channels = (int) [ 1, 32 ];"
          "audio/x-raw-int, "
179
          "endianness = (int) { " ENDIANNESS " }, "
180
          "signed = (boolean) TRUE, "
181
          "width = (int) 32, "
182
          "depth = (int) 32, "
183
          "rate = (int) [ 1, MAX ], "
184
          "channels = (int) [ 1, 32 ];"
185 186 187 188 189
          "audio/x-raw-int, "
          "signed = (boolean) FALSE, "
          "width = (int) 8, "
          "depth = (int) 8, "
          "rate = (int) [ 1, MAX ], "
190
          "channels = (int) [ 1, 32 ];"
191 192
          "audio/x-alaw, "
          "rate = (int) [ 1, MAX], "
193
          "channels = (int) [ 1, 32 ];"
194
          "audio/x-mulaw, "
195
          "rate = (int) [ 1, MAX], " "channels = (int) [ 1, 32 ]")
196 197 198 199
      );

  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

200 201 202 203
  gst_element_class_set_details_simple (element_class,
      "PulseAudio Audio Source",
      "Source/Audio",
      "Captures audio from a PulseAudio server", "Lennart Poettering");
204 205 206 207 208
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&pad_template));
}

static void
209
gst_pulsesrc_class_init (GstPulseSrcClass * klass)
210
{
211 212 213 214
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstAudioSrcClass *gstaudiosrc_class = GST_AUDIO_SRC_CLASS (klass);
  GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
215
  gchar *clientname;
216

217 218 219
  gobject_class->finalize = gst_pulsesrc_finalize;
  gobject_class->set_property = gst_pulsesrc_set_property;
  gobject_class->get_property = gst_pulsesrc_get_property;
220

221 222 223
  gstelement_class->change_state =
      GST_DEBUG_FUNCPTR (gst_pulsesrc_change_state);

224 225
  gstbasesrc_class->negotiate = GST_DEBUG_FUNCPTR (gst_pulsesrc_negotiate);

226 227 228 229 230 231
  gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_pulsesrc_open);
  gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_pulsesrc_close);
  gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_pulsesrc_prepare);
  gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_pulsesrc_unprepare);
  gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_pulsesrc_read);
  gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_pulsesrc_delay);
232
  gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_pulsesrc_reset);
233 234 235 236 237

  /* Overwrite GObject fields */
  g_object_class_install_property (gobject_class,
      PROP_SERVER,
      g_param_spec_string ("server", "Server",
238
          "The PulseAudio server to connect to", DEFAULT_SERVER,
239
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
240

241
  g_object_class_install_property (gobject_class, PROP_DEVICE,
242 243
      g_param_spec_string ("device", "Device",
          "The PulseAudio source device to connect to", DEFAULT_DEVICE,
244
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
245 246 247 248

  g_object_class_install_property (gobject_class,
      PROP_DEVICE_NAME,
      g_param_spec_string ("device-name", "Device name",
249
          "Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
250
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
251

252
  clientname = gst_pulse_client_name ();
Andy Wingo's avatar
Andy Wingo committed
253
  /**
254
   * GstPulseSrc:client
Andy Wingo's avatar
Andy Wingo committed
255 256 257 258 259 260 261 262
   *
   * The PulseAudio client name to use.
   *
   * Since: 0.10.27
   */
  g_object_class_install_property (gobject_class,
      PROP_CLIENT,
      g_param_spec_string ("client", "Client",
263
          "The PulseAudio client_name_to_use", clientname,
Andy Wingo's avatar
Andy Wingo committed
264 265
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
          GST_PARAM_MUTABLE_READY));
266
  g_free (clientname);
Andy Wingo's avatar
Andy Wingo committed
267

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
  /**
   * GstPulseSrc:stream-properties
   *
   * List of pulseaudio stream properties. A list of defined properties can be
   * found in the <ulink href="http://0pointer.de/lennart/projects/pulseaudio/doxygen/proplist_8h.html">pulseaudio api docs</ulink>.
   *
   * Below is an example for registering as a music application to pulseaudio.
   * |[
   * GstStructure *props;
   *
   * props = gst_structure_from_string ("props,media.role=music", NULL);
   * g_object_set (pulse, "stream-properties", props, NULL);
   * gst_structure_free (props);
   * ]|
   *
   * Since: 0.10.26
   */
  g_object_class_install_property (gobject_class,
      PROP_STREAM_PROPERTIES,
      g_param_spec_boxed ("stream-properties", "stream properties",
          "list of pulseaudio stream properties",
          GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
290 291 292 293 294 295 296 297 298 299 300 301 302 303

  /**
   * GstPulseSrc:source-output-index
   *
   * The index of the PulseAudio source output corresponding to this element.
   *
   * Since: 0.10.31
   */
  g_object_class_install_property (gobject_class,
      PROP_SOURCE_OUTPUT_INDEX,
      g_param_spec_uint ("source-output-index", "source output index",
          "The index of the PulseAudio source output corresponding to this "
          "record stream", 0, G_MAXUINT, PA_INVALID_INDEX,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
304 305 306
}

static void
307
gst_pulsesrc_init (GstPulseSrc * pulsesrc, GstPulseSrcClass * klass)
308
{
309 310
  pulsesrc->server = NULL;
  pulsesrc->device = NULL;
Andy Wingo's avatar
Andy Wingo committed
311
  pulsesrc->client_name = gst_pulse_client_name ();
312
  pulsesrc->device_description = NULL;
313 314 315

  pulsesrc->context = NULL;
  pulsesrc->stream = NULL;
316
  pulsesrc->source_output_idx = PA_INVALID_INDEX;
317 318 319 320

  pulsesrc->read_buffer = NULL;
  pulsesrc->read_buffer_length = 0;

321 322 323
  pa_sample_spec_init (&pulsesrc->sample_spec);

  pulsesrc->operation_success = FALSE;
Wim Taymans's avatar
Wim Taymans committed
324
  pulsesrc->paused = FALSE;
325 326
  pulsesrc->in_read = FALSE;

327
  pulsesrc->mixer = NULL;
328

329 330 331
  pulsesrc->properties = NULL;
  pulsesrc->proplist = NULL;

332
  pulsesrc->probe = gst_pulseprobe_new (G_OBJECT (pulsesrc), G_OBJECT_GET_CLASS (pulsesrc), PROP_DEVICE, pulsesrc->server, FALSE, TRUE);        /* FALSE for sinks, TRUE for sources */
333 334 335 336

  /* this should be the default but it isn't yet */
  gst_base_audio_src_set_slave_method (GST_BASE_AUDIO_SRC (pulsesrc),
      GST_BASE_AUDIO_SRC_SLAVE_SKEW);
337 338 339 340 341 342 343 344 345
}

static void
gst_pulsesrc_destroy_stream (GstPulseSrc * pulsesrc)
{
  if (pulsesrc->stream) {
    pa_stream_disconnect (pulsesrc->stream);
    pa_stream_unref (pulsesrc->stream);
    pulsesrc->stream = NULL;
346 347
    pulsesrc->source_output_idx = PA_INVALID_INDEX;
    g_object_notify (G_OBJECT (pulsesrc), "source-output-index");
348
  }
349 350 351

  g_free (pulsesrc->device_description);
  pulsesrc->device_description = NULL;
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
}

static void
gst_pulsesrc_destroy_context (GstPulseSrc * pulsesrc)
{

  gst_pulsesrc_destroy_stream (pulsesrc);

  if (pulsesrc->context) {
    pa_context_disconnect (pulsesrc->context);
    pa_context_unref (pulsesrc->context);
    pulsesrc->context = NULL;
  }
}

static void
gst_pulsesrc_finalize (GObject * object)
{
370
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (object);
371 372 373

  g_free (pulsesrc->server);
  g_free (pulsesrc->device);
Andy Wingo's avatar
Andy Wingo committed
374
  g_free (pulsesrc->client_name);
375

376 377 378 379 380
  if (pulsesrc->properties)
    gst_structure_free (pulsesrc->properties);
  if (pulsesrc->proplist)
    pa_proplist_free (pulsesrc->proplist);

381
  if (pulsesrc->mixer) {
382
    gst_pulsemixer_ctrl_free (pulsesrc->mixer);
383 384
    pulsesrc->mixer = NULL;
  }
385

386 387 388 389 390
  if (pulsesrc->probe) {
    gst_pulseprobe_free (pulsesrc->probe);
    pulsesrc->probe = NULL;
  }

391 392 393
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

394 395 396
#define CONTEXT_OK(c) ((c) && PA_CONTEXT_IS_GOOD (pa_context_get_state ((c))))
#define STREAM_OK(s) ((s) && PA_STREAM_IS_GOOD (pa_stream_get_state ((s))))

397
static gboolean
398
gst_pulsesrc_is_dead (GstPulseSrc * pulsesrc, gboolean check_stream)
399
{
400 401 402 403 404 405 406
  if (!CONTEXT_OK (pulsesrc->context))
    goto error;

  if (check_stream && !STREAM_OK (pulsesrc->stream))
    goto error;

  return FALSE;
407

408 409
error:
  {
410 411 412 413 414 415
    const gchar *err_str = pulsesrc->context ?
        pa_strerror (pa_context_errno (pulsesrc->context)) : NULL;
    GST_ELEMENT_ERROR ((pulsesrc), RESOURCE, FAILED, ("Disconnected: %s",
            err_str), (NULL));
    return TRUE;
  }
416 417
}

418 419 420 421
static void
gst_pulsesrc_source_info_cb (pa_context * c, const pa_source_info * i, int eol,
    void *userdata)
{
422
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (userdata);
423 424

  if (!i)
425
    goto done;
426 427 428

  g_free (pulsesrc->device_description);
  pulsesrc->device_description = g_strdup (i->description);
429 430 431

done:
  pa_threaded_mainloop_signal (pulsesrc->mainloop, 0);
432 433 434 435 436 437 438 439
}

static gchar *
gst_pulsesrc_device_description (GstPulseSrc * pulsesrc)
{
  pa_operation *o = NULL;
  gchar *t;

440 441 442
  if (!pulsesrc->mainloop)
    goto no_mainloop;

443 444
  pa_threaded_mainloop_lock (pulsesrc->mainloop);

445 446
  if (!(o = pa_context_get_source_info_by_name (pulsesrc->context,
              pulsesrc->device, gst_pulsesrc_source_info_cb, pulsesrc))) {
447 448 449 450 451 452 453 454 455

    GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
        ("pa_stream_get_source_info() failed: %s",
            pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
    goto unlock;
  }

  while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {

456
    if (gst_pulsesrc_is_dead (pulsesrc, FALSE))
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
      goto unlock;

    pa_threaded_mainloop_wait (pulsesrc->mainloop);
  }

unlock:

  if (o)
    pa_operation_unref (o);

  t = g_strdup (pulsesrc->device_description);

  pa_threaded_mainloop_unlock (pulsesrc->mainloop);

  return t;
472 473 474 475 476 477

no_mainloop:
  {
    GST_DEBUG_OBJECT (pulsesrc, "have no mainloop");
    return NULL;
  }
478 479
}

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
static void
gst_pulsesrc_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec)
{

  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (object);

  switch (prop_id) {
    case PROP_SERVER:
      g_free (pulsesrc->server);
      pulsesrc->server = g_value_dup_string (value);
      if (pulsesrc->probe)
        gst_pulseprobe_set_server (pulsesrc->probe, pulsesrc->server);
      break;
    case PROP_DEVICE:
      g_free (pulsesrc->device);
      pulsesrc->device = g_value_dup_string (value);
      break;
Andy Wingo's avatar
Andy Wingo committed
498 499 500 501 502 503 504 505 506
    case PROP_CLIENT:
      g_free (pulsesrc->client_name);
      if (!g_value_get_string (value)) {
        GST_WARNING_OBJECT (pulsesrc,
            "Empty PulseAudio client name not allowed. Resetting to default value");
        pulsesrc->client_name = gst_pulse_client_name ();
      } else
        pulsesrc->client_name = g_value_dup_string (value);
      break;
507 508 509 510 511 512 513 514 515
    case PROP_STREAM_PROPERTIES:
      if (pulsesrc->properties)
        gst_structure_free (pulsesrc->properties);
      pulsesrc->properties =
          gst_structure_copy (gst_value_get_structure (value));
      if (pulsesrc->proplist)
        pa_proplist_free (pulsesrc->proplist);
      pulsesrc->proplist = gst_pulse_make_proplist (pulsesrc->properties);
      break;
516 517 518 519 520 521
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

522 523 524 525 526
static void
gst_pulsesrc_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
{

527
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (object);
528 529 530 531 532 533 534 535

  switch (prop_id) {
    case PROP_SERVER:
      g_value_set_string (value, pulsesrc->server);
      break;
    case PROP_DEVICE:
      g_value_set_string (value, pulsesrc->device);
      break;
536 537
    case PROP_DEVICE_NAME:
      g_value_take_string (value, gst_pulsesrc_device_description (pulsesrc));
538
      break;
Andy Wingo's avatar
Andy Wingo committed
539 540 541
    case PROP_CLIENT:
      g_value_set_string (value, pulsesrc->client_name);
      break;
542 543 544
    case PROP_STREAM_PROPERTIES:
      gst_value_set_structure (value, pulsesrc->properties);
      break;
545 546 547
    case PROP_SOURCE_OUTPUT_INDEX:
      g_value_set_uint (value, pulsesrc->source_output_idx);
      break;
548 549 550 551 552 553 554 555 556
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_pulsesrc_context_state_cb (pa_context * c, void *userdata)
{
557
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (userdata);
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576

  switch (pa_context_get_state (c)) {
    case PA_CONTEXT_READY:
    case PA_CONTEXT_TERMINATED:
    case PA_CONTEXT_FAILED:
      pa_threaded_mainloop_signal (pulsesrc->mainloop, 0);
      break;

    case PA_CONTEXT_UNCONNECTED:
    case PA_CONTEXT_CONNECTING:
    case PA_CONTEXT_AUTHORIZING:
    case PA_CONTEXT_SETTING_NAME:
      break;
  }
}

static void
gst_pulsesrc_stream_state_cb (pa_stream * s, void *userdata)
{
577
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (userdata);
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595

  switch (pa_stream_get_state (s)) {

    case PA_STREAM_READY:
    case PA_STREAM_FAILED:
    case PA_STREAM_TERMINATED:
      pa_threaded_mainloop_signal (pulsesrc->mainloop, 0);
      break;

    case PA_STREAM_UNCONNECTED:
    case PA_STREAM_CREATING:
      break;
  }
}

static void
gst_pulsesrc_stream_request_cb (pa_stream * s, size_t length, void *userdata)
{
596 597 598
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (userdata);

  GST_LOG_OBJECT (pulsesrc, "got request for length %" G_GSIZE_FORMAT, length);
599

Wim Taymans's avatar
Wim Taymans committed
600 601 602 603
  if (pulsesrc->in_read) {
    /* only signal when reading */
    pa_threaded_mainloop_signal (pulsesrc->mainloop, 0);
  }
604 605
}

606 607 608
static void
gst_pulsesrc_stream_latency_update_cb (pa_stream * s, void *userdata)
{
609
  const pa_timing_info *info;
610
  pa_usec_t source_usec;
611 612 613

  info = pa_stream_get_timing_info (s);

614 615 616 617 618
  if (!info) {
    GST_LOG_OBJECT (GST_PULSESRC_CAST (userdata),
        "latency update (information unknown)");
    return;
  }
619 620
  source_usec = info->configured_source_usec;

621
  GST_LOG_OBJECT (GST_PULSESRC_CAST (userdata),
622 623 624 625
      "latency_update, %" G_GUINT64_FORMAT ", %d:%" G_GINT64_FORMAT ", %d:%"
      G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT,
      GST_TIMEVAL_TO_TIME (info->timestamp), info->write_index_corrupt,
      info->write_index, info->read_index_corrupt, info->read_index,
626
      info->source_usec, source_usec);
627 628
}

629 630 631
static void
gst_pulsesrc_stream_underflow_cb (pa_stream * s, void *userdata)
{
632
  GST_WARNING_OBJECT (GST_PULSESRC_CAST (userdata), "Got underflow");
633 634 635 636 637
}

static void
gst_pulsesrc_stream_overflow_cb (pa_stream * s, void *userdata)
{
638
  GST_WARNING_OBJECT (GST_PULSESRC_CAST (userdata), "Got overflow");
639 640
}

641 642 643
static gboolean
gst_pulsesrc_open (GstAudioSrc * asrc)
{
644
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (asrc);
645 646 647

  pa_threaded_mainloop_lock (pulsesrc->mainloop);

648 649 650
  g_assert (!pulsesrc->context);
  g_assert (!pulsesrc->stream);

651 652
  GST_DEBUG_OBJECT (pulsesrc, "opening device");

653 654
  if (!(pulsesrc->context =
          pa_context_new (pa_threaded_mainloop_get_api (pulsesrc->mainloop),
Andy Wingo's avatar
Andy Wingo committed
655
              pulsesrc->client_name))) {
656 657 658 659 660 661 662 663
    GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to create context"),
        (NULL));
    goto unlock_and_fail;
  }

  pa_context_set_state_callback (pulsesrc->context,
      gst_pulsesrc_context_state_cb, pulsesrc);

664 665 666
  GST_DEBUG_OBJECT (pulsesrc, "connect to server %s",
      GST_STR_NULL (pulsesrc->server));

667 668 669 670 671 672
  if (pa_context_connect (pulsesrc->context, pulsesrc->server, 0, NULL) < 0) {
    GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to connect: %s",
            pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
    goto unlock_and_fail;
  }

673 674
  for (;;) {
    pa_context_state_t state;
675

676 677 678 679 680 681 682 683 684 685 686 687 688
    state = pa_context_get_state (pulsesrc->context);

    if (!PA_CONTEXT_IS_GOOD (state)) {
      GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to connect: %s",
              pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
      goto unlock_and_fail;
    }

    if (state == PA_CONTEXT_READY)
      break;

    /* Wait until the context is ready */
    pa_threaded_mainloop_wait (pulsesrc->mainloop);
689
  }
690
  GST_DEBUG_OBJECT (pulsesrc, "connected");
691 692 693 694 695

  pa_threaded_mainloop_unlock (pulsesrc->mainloop);

  return TRUE;

Wim Taymans's avatar
Wim Taymans committed
696
  /* ERRORS */
697
unlock_and_fail:
Wim Taymans's avatar
Wim Taymans committed
698 699
  {
    gst_pulsesrc_destroy_context (pulsesrc);
700

Wim Taymans's avatar
Wim Taymans committed
701
    pa_threaded_mainloop_unlock (pulsesrc->mainloop);
702

Wim Taymans's avatar
Wim Taymans committed
703 704
    return FALSE;
  }
705 706 707 708 709
}

static gboolean
gst_pulsesrc_close (GstAudioSrc * asrc)
{
710
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (asrc);
711 712 713 714 715 716 717 718 719 720 721

  pa_threaded_mainloop_lock (pulsesrc->mainloop);
  gst_pulsesrc_destroy_context (pulsesrc);
  pa_threaded_mainloop_unlock (pulsesrc->mainloop);

  return TRUE;
}

static gboolean
gst_pulsesrc_unprepare (GstAudioSrc * asrc)
{
722
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (asrc);
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737

  pa_threaded_mainloop_lock (pulsesrc->mainloop);
  gst_pulsesrc_destroy_stream (pulsesrc);

  pa_threaded_mainloop_unlock (pulsesrc->mainloop);

  pulsesrc->read_buffer = NULL;
  pulsesrc->read_buffer_length = 0;

  return TRUE;
}

static guint
gst_pulsesrc_read (GstAudioSrc * asrc, gpointer data, guint length)
{
738
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (asrc);
739 740 741
  size_t sum = 0;

  pa_threaded_mainloop_lock (pulsesrc->mainloop);
742
  pulsesrc->in_read = TRUE;
743

Wim Taymans's avatar
Wim Taymans committed
744 745 746
  if (pulsesrc->paused)
    goto was_paused;

747 748 749
  while (length > 0) {
    size_t l;

750
    GST_LOG_OBJECT (pulsesrc, "reading %u bytes", length);
751

Wim Taymans's avatar
Wim Taymans committed
752
    /*check if we have a leftover buffer */
753
    if (!pulsesrc->read_buffer) {
754
      for (;;) {
755
        if (gst_pulsesrc_is_dead (pulsesrc, TRUE))
756 757
          goto unlock_and_fail;

Wim Taymans's avatar
Wim Taymans committed
758 759
        /* read all available data, we keep a pointer to the data and the length
         * and take from it what we need. */
760
        if (pa_stream_peek (pulsesrc->stream, &pulsesrc->read_buffer,
Wim Taymans's avatar
Wim Taymans committed
761 762 763
                &pulsesrc->read_buffer_length) < 0)
          goto peek_failed;

764 765
        GST_LOG_OBJECT (pulsesrc, "have data of %" G_GSIZE_FORMAT " bytes",
            pulsesrc->read_buffer_length);
766

767
        /* if we have data, process if */
Wim Taymans's avatar
Wim Taymans committed
768
        if (pulsesrc->read_buffer && pulsesrc->read_buffer_length)
769 770
          break;

Wim Taymans's avatar
Wim Taymans committed
771
        /* now wait for more data to become available */
772
        GST_LOG_OBJECT (pulsesrc, "waiting for data");
773
        pa_threaded_mainloop_wait (pulsesrc->mainloop);
Wim Taymans's avatar
Wim Taymans committed
774 775 776

        if (pulsesrc->paused)
          goto was_paused;
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
      }
    }

    l = pulsesrc->read_buffer_length >
        length ? length : pulsesrc->read_buffer_length;

    memcpy (data, pulsesrc->read_buffer, l);

    pulsesrc->read_buffer = (const guint8 *) pulsesrc->read_buffer + l;
    pulsesrc->read_buffer_length -= l;

    data = (guint8 *) data + l;
    length -= l;
    sum += l;

    if (pulsesrc->read_buffer_length <= 0) {
Wim Taymans's avatar
Wim Taymans committed
793 794 795
      /* we copied all of the data, drop it now */
      if (pa_stream_drop (pulsesrc->stream) < 0)
        goto drop_failed;
796

Wim Taymans's avatar
Wim Taymans committed
797
      /* reset pointer to data */
798 799 800 801 802
      pulsesrc->read_buffer = NULL;
      pulsesrc->read_buffer_length = 0;
    }
  }

803 804
  pulsesrc->in_read = FALSE;
  pa_threaded_mainloop_unlock (pulsesrc->mainloop);
Wim Taymans's avatar
Wim Taymans committed
805

806 807
  return sum;

808
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
was_paused:
  {
    GST_LOG_OBJECT (pulsesrc, "we are paused");
    goto unlock_and_fail;
  }
peek_failed:
  {
    GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
        ("pa_stream_peek() failed: %s",
            pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
    goto unlock_and_fail;
  }
drop_failed:
  {
    GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
        ("pa_stream_drop() failed: %s",
            pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
    goto unlock_and_fail;
  }
828
unlock_and_fail:
829 830 831
  {
    pulsesrc->in_read = FALSE;
    pa_threaded_mainloop_unlock (pulsesrc->mainloop);
Wim Taymans's avatar
Wim Taymans committed
832

833 834
    return (guint) - 1;
  }
835 836
}

Wim Taymans's avatar
Wim Taymans committed
837
/* return the delay in samples */
838 839 840
static guint
gst_pulsesrc_delay (GstAudioSrc * asrc)
{
841
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (asrc);
842
  pa_usec_t t;
Wim Taymans's avatar
Wim Taymans committed
843 844
  int negative, res;
  guint result;
845 846

  pa_threaded_mainloop_lock (pulsesrc->mainloop);
847
  if (gst_pulsesrc_is_dead (pulsesrc, TRUE))
Wim Taymans's avatar
Wim Taymans committed
848
    goto server_dead;
849

Wim Taymans's avatar
Wim Taymans committed
850 851 852
  /* get the latency, this can fail when we don't have a latency update yet.
   * We don't want to wait for latency updates here but we just return 0. */
  res = pa_stream_get_latency (pulsesrc->stream, &t, &negative);
853 854 855

  pa_threaded_mainloop_unlock (pulsesrc->mainloop);

Wim Taymans's avatar
Wim Taymans committed
856 857 858 859 860 861 862 863 864 865
  if (res > 0) {
    GST_DEBUG_OBJECT (pulsesrc, "could not get latency");
    result = 0;
  } else {
    if (negative)
      result = 0;
    else
      result = (guint) ((t * pulsesrc->sample_spec.rate) / 1000000LL);
  }
  return result;
866

Wim Taymans's avatar
Wim Taymans committed
867 868 869 870 871 872 873
  /* ERRORS */
server_dead:
  {
    GST_DEBUG_OBJECT (pulsesrc, "the server is dead");
    pa_threaded_mainloop_unlock (pulsesrc->mainloop);
    return 0;
  }
874 875
}

876 877 878 879 880 881 882
static gboolean
gst_pulsesrc_create_stream (GstPulseSrc * pulsesrc, GstCaps * caps)
{
  pa_channel_map channel_map;
  GstStructure *s;
  gboolean need_channel_layout = FALSE;
  GstRingBufferSpec spec;
883
  const gchar *name;
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902

  memset (&spec, 0, sizeof (GstRingBufferSpec));
  spec.latency_time = GST_SECOND;
  if (!gst_ring_buffer_parse_caps (&spec, caps)) {
    GST_ELEMENT_ERROR (pulsesrc, RESOURCE, SETTINGS,
        ("Can't parse caps."), (NULL));
    goto fail;
  }
  /* Keep the refcount of the caps at 1 to make them writable */
  gst_caps_unref (spec.caps);

  if (!gst_pulse_fill_sample_spec (&spec, &pulsesrc->sample_spec)) {
    GST_ELEMENT_ERROR (pulsesrc, RESOURCE, SETTINGS,
        ("Invalid sample specification."), (NULL));
    goto fail;
  }

  pa_threaded_mainloop_lock (pulsesrc->mainloop);

903 904
  if (!pulsesrc->context) {
    GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Bad context"), (NULL));
905 906 907 908 909 910 911 912 913 914 915 916 917 918
    goto unlock_and_fail;
  }

  s = gst_caps_get_structure (caps, 0);
  if (!gst_structure_has_field (s, "channel-layout") ||
      !gst_pulse_gst_to_channel_map (&channel_map, &spec)) {
    if (spec.channels == 1)
      pa_channel_map_init_mono (&channel_map);
    else if (spec.channels == 2)
      pa_channel_map_init_stereo (&channel_map);
    else
      need_channel_layout = TRUE;
  }

919 920 921 922 923 924 925 926 927 928 929 930 931
  name = "Record Stream";
  if (pulsesrc->proplist) {
    if (!(pulsesrc->stream = pa_stream_new_with_proplist (pulsesrc->context,
                name, &pulsesrc->sample_spec,
                (need_channel_layout) ? NULL : &channel_map,
                pulsesrc->proplist))) {
      GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
          ("Failed to create stream: %s",
              pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
      goto unlock_and_fail;
    }
  } else if (!(pulsesrc->stream = pa_stream_new (pulsesrc->context,
              name, &pulsesrc->sample_spec,
932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
              (need_channel_layout) ? NULL : &channel_map))) {
    GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
        ("Failed to create stream: %s",
            pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
    goto unlock_and_fail;
  }

  if (need_channel_layout) {
    const pa_channel_map *m = pa_stream_get_channel_map (pulsesrc->stream);

    gst_pulse_channel_map_to_gst (m, &spec);
    caps = spec.caps;
  }

  GST_DEBUG_OBJECT (pulsesrc, "Caps are %" GST_PTR_FORMAT, caps);

  pa_stream_set_state_callback (pulsesrc->stream, gst_pulsesrc_stream_state_cb,
      pulsesrc);
  pa_stream_set_read_callback (pulsesrc->stream, gst_pulsesrc_stream_request_cb,
      pulsesrc);
952 953 954 955
  pa_stream_set_underflow_callback (pulsesrc->stream,
      gst_pulsesrc_stream_underflow_cb, pulsesrc);
  pa_stream_set_overflow_callback (pulsesrc->stream,
      gst_pulsesrc_stream_overflow_cb, pulsesrc);
956 957 958 959
  pa_stream_set_latency_update_callback (pulsesrc->stream,
      gst_pulsesrc_stream_latency_update_cb, pulsesrc);

  pa_threaded_mainloop_unlock (pulsesrc->mainloop);
960 961 962 963

  return TRUE;

unlock_and_fail:
964 965
  gst_pulsesrc_destroy_stream (pulsesrc);

966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983
  pa_threaded_mainloop_unlock (pulsesrc->mainloop);

fail:
  return FALSE;
}

/* This is essentially gst_base_src_negotiate_default() but the caps
 * are guaranteed to have a channel layout for > 2 channels
 */
static gboolean
gst_pulsesrc_negotiate (GstBaseSrc * basesrc)
{
  GstCaps *thiscaps;
  GstCaps *caps = NULL;
  GstCaps *peercaps = NULL;
  gboolean result = FALSE;

  /* first see what is possible on our source pad */
984
  thiscaps = gst_pad_get_caps_reffed (GST_BASE_SRC_PAD (basesrc));
985 986 987 988 989 990
  GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
  /* nothing or anything is allowed, we're done */
  if (thiscaps == NULL || gst_caps_is_any (thiscaps))
    goto no_nego_needed;

  /* get the peer caps */
991
  peercaps = gst_pad_peer_get_caps_reffed (GST_BASE_SRC_PAD (basesrc));
992 993 994
  GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
  if (peercaps) {
    /* get intersection */
995 996
    caps = gst_caps_intersect (thiscaps, peercaps);
    GST_DEBUG_OBJECT (basesrc, "intersect: %" GST_PTR_FORMAT, caps);
997 998 999 1000 1001 1002 1003
    gst_caps_unref (thiscaps);
    gst_caps_unref (peercaps);
  } else {
    /* no peer, work with our own caps then */
    caps = thiscaps;
  }
  if (caps) {
1004
    /* take first (and best, since they are sorted) possibility */
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
    caps = gst_caps_make_writable (caps);
    gst_caps_truncate (caps);

    /* now fixate */
    if (!gst_caps_is_empty (caps)) {
      gst_pad_fixate_caps (GST_BASE_SRC_PAD (basesrc), caps);
      GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps);

      if (gst_caps_is_any (caps)) {
        /* hmm, still anything, so element can do anything and
         * nego is not needed */
        result = TRUE;
      } else if (gst_caps_is_fixed (caps)) {
        /* yay, fixed caps, use those then */
1019
        result = gst_pulsesrc_create_stream (GST_PULSESRC_CAST (basesrc), caps);
1020
        if (result)
1021
          result = gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), caps);
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
      }
    }
    gst_caps_unref (caps);
  }
  return result;

no_nego_needed:
  {
    GST_DEBUG_OBJECT (basesrc, "no negotiation needed");
    if (thiscaps)
      gst_caps_unref (thiscaps);
    return TRUE;
  }
}

static gboolean
gst_pulsesrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
{
1040 1041
  pa_buffer_attr wanted;
  const pa_buffer_attr *actual;
1042
  GstPulseSrc *pulsesrc = GST_PULSESRC_CAST (asrc);
1043

1044 1045
  pa_threaded_mainloop_lock (pulsesrc->mainloop);

1046 1047 1048 1049 1050
  wanted.maxlength = -1;
  wanted.tlength = -1;
  wanted.prebuf = 0;
  wanted.minreq = -1;
  wanted.fragsize = spec->segsize;
1051

1052 1053 1054 1055 1056
  GST_INFO_OBJECT (pulsesrc, "maxlength: %d", wanted.maxlength);
  GST_INFO_OBJECT (pulsesrc, "tlength:   %d", wanted.tlength);
  GST_INFO_OBJECT (pulsesrc, "prebuf:    %d", wanted.prebuf);
  GST_INFO_OBJECT (pulsesrc, "minreq:    %d", wanted.minreq);
  GST_INFO_OBJECT (pulsesrc, "fragsize:  %d", wanted.fragsize);
1057

1058
  if (pa_stream_connect_record (pulsesrc->stream, pulsesrc->device, &wanted,
1059 1060
          PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE |
          PA_STREAM_NOT_MONOTONIC | PA_STREAM_ADJUST_LATENCY |
1061
          PA_STREAM_START_CORKED) < 0) {
1062 1063 1064 1065 1066 1067
    GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED,
        ("Failed to connect stream: %s",
            pa_strerror (pa_context_errno (pulsesrc->context))), (NULL));
    goto unlock_and_fail;
  }

Wim Taymans's avatar
Wim Taymans committed
1068 1069
  pulsesrc->corked = TRUE;

1070 1071
  for (;;) {
    pa_stream_state_t state;
1072

1073