Beispiel #1
0
static void pulse_stop_playback(ALCdevice *device) //{{{
{
    pulse_data *data = device->ExtraData;

    if(!data->stream)
        return;

    data->killNow = AL_TRUE;
    if(data->thread)
    {
        pa_threaded_mainloop_signal(data->loop, 0);
        StopThread(data->thread);
        data->thread = NULL;
    }
    data->killNow = AL_FALSE;

    pa_threaded_mainloop_lock(data->loop);

#if PA_CHECK_VERSION(0,9,15)
    if(pa_stream_set_buffer_attr_callback)
        pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
#endif
    pa_stream_set_moved_callback(data->stream, NULL, NULL);
    pa_stream_set_write_callback(data->stream, NULL, NULL);
    pa_stream_set_underflow_callback(data->stream, NULL, NULL);
    pa_stream_disconnect(data->stream);
    pa_stream_unref(data->stream);
    data->stream = NULL;

    pa_threaded_mainloop_unlock(data->loop);
} //}}}
Beispiel #2
0
static void pulse_close(ALCdevice *device)
{
    pulse_data *data = device->ExtraData;

    pa_threaded_mainloop_lock(data->loop);

    if(data->stream)
    {
        pa_stream_set_moved_callback(data->stream, NULL, NULL);
#if PA_CHECK_VERSION(0,9,15)
        if(pa_stream_set_buffer_attr_callback)
            pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
#endif
        pa_stream_disconnect(data->stream);
        pa_stream_unref(data->stream);
    }

    pa_context_disconnect(data->context);
    pa_context_unref(data->context);

    pa_threaded_mainloop_unlock(data->loop);

    pa_threaded_mainloop_stop(data->loop);
    pa_threaded_mainloop_free(data->loop);

    free(data->device_name);

    device->ExtraData = NULL;
    pa_xfree(data);
}
void QPulseAudioThread::reconnect(SourceContainer::const_iterator pos = s_sourceList.end())
{

    if (s_sourceList.empty())
        return;

    if (pos != s_sourceList.end()) {
        s_sourcePosition = pos;
        qDebug() << "reconnecting with" << *pos;
    } else
        s_sourcePosition = scanForPlaybackMonitor();

    if (s_sourcePosition == s_sourceList.end()) {
        s_sourcePosition = s_sourceList.begin();
    }

    if (stream && (pa_stream_get_state(stream) == PA_STREAM_READY)) {
        //qDebug() << "disconnect";
        pa_stream_disconnect ( stream );
        //	pa_stream_unref(stream);
        //qDebug() << "* return *";

    }

    if ( ! ( stream = pa_stream_new ( context, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL ) ) ) {
        fprintf ( stderr, "pa_stream_new() failed: %s\n", pa_strerror ( pa_context_errno ( context ) ) );
        return;
    }

    pa_stream_set_state_callback
    ( stream, stream_state_callback, &s_sourceList );
    pa_stream_set_read_callback ( stream, stream_read_callback, &s_sourceList );
    pa_stream_set_moved_callback(stream, stream_moved_callback, &s_sourceList );

    switch (pa_stream_get_state(stream)) {
    case PA_STREAM_UNCONNECTED:// 	The stream is not yet connected to any sink or source.
        qDebug() << "unconnected: connecting...";
        connectHelper(s_sourcePosition);
        break;
    case PA_STREAM_CREATING 	://The stream is being created.
        break;
    case PA_STREAM_READY ://	The stream is established, you may pass audio data to it now.
        qDebug() << "stream is still ready, waiting for callback...";
        break;
    case PA_STREAM_FAILED ://	An error occured that made the stream invalid.
        qDebug() << "stream is now invalid. great.";
        break;
    case PA_STREAM_TERMINATED:// 	The stream has been terminated cleanly.
        qDebug() << "terminated...";
        break;

    }
}
AudioStream::~AudioStream()
{
    PulseMainLoopLock lock(mainloop_);

    pa_stream_disconnect(audiostream_);

    // make sure we don't get any further callback
    pa_stream_set_state_callback(audiostream_, NULL, NULL);
    pa_stream_set_write_callback(audiostream_, NULL, NULL);
    pa_stream_set_read_callback(audiostream_, NULL, NULL);
    pa_stream_set_moved_callback(audiostream_, NULL, NULL);
    pa_stream_set_underflow_callback(audiostream_, NULL, NULL);
    pa_stream_set_overflow_callback(audiostream_, NULL, NULL);

    pa_stream_unref(audiostream_);
}
AudioStream::AudioStream(pa_context *c,
                         pa_threaded_mainloop *m,
                         const char *desc,
                         int type,
                         unsigned samplrate,
                         const PaDeviceInfos* infos,
                         bool ec)
    : audiostream_(0), mainloop_(m)
{
    const pa_channel_map channel_map = infos->channel_map;

    pa_sample_spec sample_spec = {
        PA_SAMPLE_S16LE, // PA_SAMPLE_FLOAT32LE,
        samplrate,
        channel_map.channels
    };

    RING_DBG("%s: trying to create stream with device %s (%dHz, %d channels)", desc, infos->name.c_str(), samplrate, channel_map.channels);

    assert(pa_sample_spec_valid(&sample_spec));
    assert(pa_channel_map_valid(&channel_map));

    std::unique_ptr<pa_proplist, decltype(pa_proplist_free)&> pl (pa_proplist_new(), pa_proplist_free);
    pa_proplist_sets(pl.get(), PA_PROP_FILTER_WANT, "echo-cancel");

    audiostream_ = pa_stream_new_with_proplist(c, desc, &sample_spec, &channel_map, ec ? pl.get() : nullptr);
    if (!audiostream_) {
        RING_ERR("%s: pa_stream_new() failed : %s" , desc, pa_strerror(pa_context_errno(c)));
        throw std::runtime_error("Could not create stream\n");
    }

    pa_buffer_attr attributes;
    attributes.maxlength = pa_usec_to_bytes(160 * PA_USEC_PER_MSEC, &sample_spec);
    attributes.tlength = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec);
    attributes.prebuf = 0;
    attributes.fragsize = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec);
    attributes.minreq = (uint32_t) -1;

    {
        PulseMainLoopLock lock(mainloop_);
        const pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);

        if (type == PLAYBACK_STREAM || type == RINGTONE_STREAM) {
            pa_stream_connect_playback(audiostream_,
                    infos->name.empty() ? NULL : infos->name.c_str(),
                    &attributes,
                    flags,
                    NULL, NULL);
        } else if (type == CAPTURE_STREAM) {
            pa_stream_connect_record(audiostream_,
                    infos->name.empty() ? NULL : infos->name.c_str(),
                    &attributes,
                    flags);
        }
    }

    pa_stream_set_state_callback(audiostream_, [](pa_stream* s, void* user_data){
        static_cast<AudioStream*>(user_data)->stateChanged(s);
    }, this);
    pa_stream_set_moved_callback(audiostream_, [](pa_stream* s, void* user_data){
        static_cast<AudioStream*>(user_data)->moved(s);
    }, this);
}
// This callback gets called when our context changes state.  We really only
// care about when it's ready or if it has failed
void state_cb(pa_context *c, void *userdata) {
  pa_context_state_t state;
  int *pa_ready = userdata;

  printf("State changed\n");
  state = pa_context_get_state(c);
  switch  (state) {
    // There are just here for reference
  case PA_CONTEXT_UNCONNECTED:
  case PA_CONTEXT_CONNECTING:
  case PA_CONTEXT_AUTHORIZING:
  case PA_CONTEXT_SETTING_NAME:
  default:
    break;
  case PA_CONTEXT_FAILED:
  case PA_CONTEXT_TERMINATED:
    *pa_ready = 2;
    break;
  case PA_CONTEXT_READY: {
    pa_buffer_attr buffer_attr;

    if (verbose)
      printf("Connection established.%s\n", CLEAR_LINE);

    if (!(stream = pa_stream_new(c, "JanPlayback", &sample_spec, NULL))) {
      printf("pa_stream_new() failed: %s", pa_strerror(pa_context_errno(c)));
      exit(1); // goto fail;
    }

    pa_stream_set_state_callback(stream, stream_state_callback, NULL);
    
    pa_stream_set_write_callback(stream, stream_write_callback, NULL);
    
    //pa_stream_set_read_callback(stream, stream_read_callback, NULL);
    
    pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
    pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
    pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
    pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
    
    pa_stream_set_started_callback(stream, stream_started_callback, NULL);
    
    pa_stream_set_event_callback(stream, stream_event_callback, NULL);
    pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL);
    
    

    pa_zero(buffer_attr);
    buffer_attr.maxlength = (uint32_t) -1;
    buffer_attr.prebuf = (uint32_t) -1;
    
    pa_cvolume cv;
      



    if (pa_stream_connect_playback(stream, NULL, &buffer_attr, flags,
				   NULL, 
				   NULL) < 0) {
      printf("pa_stream_connect_playback() failed: %s", pa_strerror(pa_context_errno(c)));
      exit(1); //goto fail;
    } else {
      printf("Set playback callback\n");
    }

    pa_stream_trigger(stream, stream_success, NULL);
  }

    break;
  }
}
Beispiel #7
0
/* This is called whenever the context status changes */
static void context_state_callback(pa_context *c, void *userdata) {
    pa_assert(c);

    switch (pa_context_get_state(c)) {
        case PA_CONTEXT_CONNECTING:
        case PA_CONTEXT_AUTHORIZING:
        case PA_CONTEXT_SETTING_NAME:
            break;

        case PA_CONTEXT_READY: {
            pa_buffer_attr buffer_attr;

            pa_assert(c);
            pa_assert(!stream);

            if (verbose)
                pa_log(_("Connection established.%s"), CLEAR_LINE);

            if (!(stream = pa_stream_new_with_proplist(c, NULL, &sample_spec, &channel_map, proplist))) {
                pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c)));
                goto fail;
            }

            pa_stream_set_state_callback(stream, stream_state_callback, NULL);
            pa_stream_set_write_callback(stream, stream_write_callback, NULL);
            pa_stream_set_read_callback(stream, stream_read_callback, NULL);
            pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
            pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
            pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
            pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
            pa_stream_set_started_callback(stream, stream_started_callback, NULL);
            pa_stream_set_event_callback(stream, stream_event_callback, NULL);
            pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL);

            pa_zero(buffer_attr);
            buffer_attr.maxlength = (uint32_t) -1;
            buffer_attr.prebuf = (uint32_t) -1;

            if (latency_msec > 0) {
                buffer_attr.fragsize = buffer_attr.tlength = pa_usec_to_bytes(latency_msec * PA_USEC_PER_MSEC, &sample_spec);
                flags |= PA_STREAM_ADJUST_LATENCY;
            } else if (latency > 0) {
                buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) latency;
                flags |= PA_STREAM_ADJUST_LATENCY;
            } else
                buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) -1;

            if (process_time_msec > 0) {
                buffer_attr.minreq = pa_usec_to_bytes(process_time_msec * PA_USEC_PER_MSEC, &sample_spec);
            } else if (process_time > 0)
                buffer_attr.minreq = (uint32_t) process_time;
            else
                buffer_attr.minreq = (uint32_t) -1;

            if (mode == PLAYBACK) {
                pa_cvolume cv;
                if (pa_stream_connect_playback(stream, device, &buffer_attr, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL) < 0) {
                    pa_log(_("pa_stream_connect_playback() failed: %s"), pa_strerror(pa_context_errno(c)));
                    goto fail;
                }

            } else {
                if (pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags) < 0) {
                    pa_log(_("pa_stream_connect_record() failed: %s"), pa_strerror(pa_context_errno(c)));
                    goto fail;
                }
            }

            break;
        }

        case PA_CONTEXT_TERMINATED:
            quit(0);
            break;

        case PA_CONTEXT_FAILED:
        default:
            pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
            goto fail;
    }

    return;

fail:
    quit(1);

}
Beispiel #8
0
static ALCboolean pulse_reset_playback(ALCdevice *device) //{{{
{
    pulse_data *data = device->ExtraData;
    pa_stream_flags_t flags = 0;
    pa_channel_map chanmap;

    pa_threaded_mainloop_lock(data->loop);

    if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
    {
        pa_operation *o;
        o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device);
        while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
            pa_threaded_mainloop_wait(data->loop);
        pa_operation_unref(o);
    }
    if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
        flags |= PA_STREAM_FIX_RATE;

    data->frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
    data->attr.prebuf = -1;
    data->attr.fragsize = -1;
    data->attr.minreq = device->UpdateSize * data->frame_size;
    data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2);
    data->attr.maxlength = -1;
    flags |= PA_STREAM_EARLY_REQUESTS;
    flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;

    switch(device->FmtType)
    {
        case DevFmtByte:
            device->FmtType = DevFmtUByte;
            /* fall-through */
        case DevFmtUByte:
            data->spec.format = PA_SAMPLE_U8;
            break;
        case DevFmtUShort:
            device->FmtType = DevFmtShort;
            /* fall-through */
        case DevFmtShort:
            data->spec.format = PA_SAMPLE_S16NE;
            break;
        case DevFmtFloat:
            data->spec.format = PA_SAMPLE_FLOAT32NE;
            break;
    }
    data->spec.rate = device->Frequency;
    data->spec.channels = ChannelsFromDevFmt(device->FmtChans);

    if(pa_sample_spec_valid(&data->spec) == 0)
    {
        ERR("Invalid sample format\n");
        pa_threaded_mainloop_unlock(data->loop);
        return ALC_FALSE;
    }

    if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
    {
        ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
        pa_threaded_mainloop_unlock(data->loop);
        return ALC_FALSE;
    }
    SetDefaultWFXChannelOrder(device);

    data->stream = connect_playback_stream(device, flags, &data->attr, &data->spec, &chanmap);
    if(!data->stream)
    {
        pa_threaded_mainloop_unlock(data->loop);
        return ALC_FALSE;
    }

    pa_stream_set_state_callback(data->stream, stream_state_callback2, device);

    data->spec = *(pa_stream_get_sample_spec(data->stream));
    if(device->Frequency != data->spec.rate)
    {
        pa_operation *o;

        if((device->Flags&DEVICE_FREQUENCY_REQUEST))
            ERR("Failed to set frequency %dhz, got %dhz instead\n", device->Frequency, data->spec.rate);
        device->Flags &= ~DEVICE_FREQUENCY_REQUEST;

        /* Server updated our playback rate, so modify the buffer attribs
         * accordingly. */
        data->attr.minreq = (ALuint64)(data->attr.minreq/data->frame_size) *
                            data->spec.rate / device->Frequency * data->frame_size;
        data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2);

        o = pa_stream_set_buffer_attr(data->stream, &data->attr,
                                      stream_success_callback, device);
        while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
            pa_threaded_mainloop_wait(data->loop);
        pa_operation_unref(o);

        device->Frequency = data->spec.rate;
    }

#if PA_CHECK_VERSION(0,9,15)
    if(pa_stream_set_buffer_attr_callback)
        pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
#endif
    pa_stream_set_moved_callback(data->stream, stream_device_callback, device);
    pa_stream_set_write_callback(data->stream, stream_write_callback, device);
    pa_stream_set_underflow_callback(data->stream, stream_signal_callback, device);

    data->attr = *(pa_stream_get_buffer_attr(data->stream));
    ERR("PulseAudio returned minreq=%d, tlength=%d\n", data->attr.minreq, data->attr.tlength);
    device->UpdateSize = data->attr.minreq / data->frame_size;
    device->NumUpdates = (data->attr.tlength/data->frame_size) / device->UpdateSize;
    while(device->NumUpdates <= 2)
    {
        pa_operation *o;

        ERR("minreq too high - expect lag or break up\n");

        /* Server gave a comparatively large minreq, so modify the tlength. */
        device->NumUpdates = 2;
        data->attr.tlength = data->attr.minreq * device->NumUpdates;

        o = pa_stream_set_buffer_attr(data->stream, &data->attr,
                                      stream_success_callback, device);

        while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
            pa_threaded_mainloop_wait(data->loop);
        pa_operation_unref(o);

        data->attr = *(pa_stream_get_buffer_attr(data->stream));
        ERR("PulseAudio returned minreq=%d, tlength=%d", data->attr.minreq, data->attr.tlength);
        device->UpdateSize = data->attr.minreq / data->frame_size;
        device->NumUpdates = (data->attr.tlength/data->frame_size) / device->UpdateSize;
    }


    data->thread = StartThread(PulseProc, device);
    if(!data->thread)
    {
#if PA_CHECK_VERSION(0,9,15)
        if(pa_stream_set_buffer_attr_callback)
            pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
#endif
        pa_stream_set_moved_callback(data->stream, NULL, NULL);
        pa_stream_set_write_callback(data->stream, NULL, NULL);
        pa_stream_set_underflow_callback(data->stream, NULL, NULL);
        pa_stream_disconnect(data->stream);
        pa_stream_unref(data->stream);
        data->stream = NULL;

        pa_threaded_mainloop_unlock(data->loop);
        return ALC_FALSE;
    }

    pa_threaded_mainloop_unlock(data->loop);
    return ALC_TRUE;
} //}}}
Beispiel #9
0
/* OpenAL */
static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name)
{
    const char *pulse_name = NULL;
    pa_stream_flags_t flags;
    pa_sample_spec spec;
    pulse_data *data;
    pa_operation *o;

    if(device_name)
    {
        ALuint i;

        if(!allDevNameMap)
            probe_devices(AL_FALSE);

        for(i = 0; i < numDevNames; i++)
        {
            if(strcmp(device_name, allDevNameMap[i].name) == 0)
            {
                pulse_name = allDevNameMap[i].device_name;
                break;
            }
        }
        if(i == numDevNames)
            return ALC_INVALID_VALUE;
    }

    if(pulse_open(device) == ALC_FALSE)
        return ALC_INVALID_VALUE;

    data = device->ExtraData;
    pa_threaded_mainloop_lock(data->loop);

    flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
            PA_STREAM_FIX_CHANNELS;
    if(!GetConfigValueBool("pulse", "allow-moves", 0))
        flags |= PA_STREAM_DONT_MOVE;

    spec.format = PA_SAMPLE_S16NE;
    spec.rate = 44100;
    spec.channels = 2;

    data->stream = connect_playback_stream(pulse_name, data->loop, data->context,
                                           flags, NULL, &spec, NULL);
    if(!data->stream)
    {
        pa_threaded_mainloop_unlock(data->loop);
        pulse_close(device);
        return ALC_INVALID_VALUE;
    }

    data->device_name = strdup(pa_stream_get_device_name(data->stream));
    o = pa_context_get_sink_info_by_name(data->context, data->device_name,
                                         sink_name_callback, device);
    wait_for_operation(o, data->loop);

    pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);

    pa_threaded_mainloop_unlock(data->loop);

    return ALC_NO_ERROR;
}
Beispiel #10
0
static ALCenum pulse_open_capture(ALCdevice *device, const ALCchar *device_name)
{
    const char *pulse_name = NULL;
    pa_stream_flags_t flags = 0;
    pa_channel_map chanmap;
    pulse_data *data;
    pa_operation *o;
    ALuint samples;

    if(device_name)
    {
        ALuint i;

        if(!allCaptureDevNameMap)
            probe_devices(AL_TRUE);

        for(i = 0; i < numCaptureDevNames; i++)
        {
            if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0)
            {
                pulse_name = allCaptureDevNameMap[i].device_name;
                break;
            }
        }
        if(i == numCaptureDevNames)
            return ALC_INVALID_VALUE;
    }

    if(pulse_open(device) == ALC_FALSE)
        return ALC_INVALID_VALUE;

    data = device->ExtraData;
    pa_threaded_mainloop_lock(data->loop);

    data->spec.rate = device->Frequency;
    data->spec.channels = ChannelsFromDevFmt(device->FmtChans);

    switch(device->FmtType)
    {
    case DevFmtUByte:
        data->spec.format = PA_SAMPLE_U8;
        break;
    case DevFmtShort:
        data->spec.format = PA_SAMPLE_S16NE;
        break;
    case DevFmtInt:
        data->spec.format = PA_SAMPLE_S32NE;
        break;
    case DevFmtFloat:
        data->spec.format = PA_SAMPLE_FLOAT32NE;
        break;
    case DevFmtByte:
    case DevFmtUShort:
    case DevFmtUInt:
        ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
        pa_threaded_mainloop_unlock(data->loop);
        goto fail;
    }

    if(pa_sample_spec_valid(&data->spec) == 0)
    {
        ERR("Invalid sample format\n");
        pa_threaded_mainloop_unlock(data->loop);
        goto fail;
    }

    if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
    {
        ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
        pa_threaded_mainloop_unlock(data->loop);
        goto fail;
    }

    samples = device->UpdateSize * device->NumUpdates;
    samples = maxu(samples, 100 * device->Frequency / 1000);

    data->attr.minreq = -1;
    data->attr.prebuf = -1;
    data->attr.maxlength = samples * pa_frame_size(&data->spec);
    data->attr.tlength = -1;
    data->attr.fragsize = minu(samples, 50*device->Frequency/1000) *
                          pa_frame_size(&data->spec);

    flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY;
    if(!GetConfigValueBool("pulse", "allow-moves", 0))
        flags |= PA_STREAM_DONT_MOVE;

    data->stream = connect_record_stream(pulse_name, data->loop, data->context,
                                         flags, &data->attr, &data->spec,
                                         &chanmap);
    if(!data->stream)
    {
        pa_threaded_mainloop_unlock(data->loop);
        goto fail;
    }
    pa_stream_set_state_callback(data->stream, stream_state_callback2, device);

    data->device_name = strdup(pa_stream_get_device_name(data->stream));
    o = pa_context_get_source_info_by_name(data->context, data->device_name,
                                           source_name_callback, device);
    wait_for_operation(o, data->loop);

    pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);

    pa_threaded_mainloop_unlock(data->loop);
    return ALC_NO_ERROR;

fail:
    pulse_close(device);
    return ALC_INVALID_VALUE;
}
Beispiel #11
0
static ALCboolean pulse_reset_playback(ALCdevice *device)
{
    pulse_data *data = device->ExtraData;
    pa_stream_flags_t flags = 0;
    pa_channel_map chanmap;
    ALuint len;

    pa_threaded_mainloop_lock(data->loop);

    if(data->stream)
    {
        pa_stream_set_moved_callback(data->stream, NULL, NULL);
#if PA_CHECK_VERSION(0,9,15)
        if(pa_stream_set_buffer_attr_callback)
            pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
#endif
        pa_stream_disconnect(data->stream);
        pa_stream_unref(data->stream);
        data->stream = NULL;
    }

    if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
    {
        pa_operation *o;
        o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device);
        wait_for_operation(o, data->loop);
    }
    if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
        flags |= PA_STREAM_FIX_RATE;

    flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
    flags |= PA_STREAM_ADJUST_LATENCY;
    flags |= PA_STREAM_START_CORKED;
    if(!GetConfigValueBool("pulse", "allow-moves", 0))
        flags |= PA_STREAM_DONT_MOVE;

    switch(device->FmtType)
    {
    case DevFmtByte:
        device->FmtType = DevFmtUByte;
    /* fall-through */
    case DevFmtUByte:
        data->spec.format = PA_SAMPLE_U8;
        break;
    case DevFmtUShort:
        device->FmtType = DevFmtShort;
    /* fall-through */
    case DevFmtShort:
        data->spec.format = PA_SAMPLE_S16NE;
        break;
    case DevFmtUInt:
        device->FmtType = DevFmtInt;
    /* fall-through */
    case DevFmtInt:
        data->spec.format = PA_SAMPLE_S32NE;
        break;
    case DevFmtFloat:
        data->spec.format = PA_SAMPLE_FLOAT32NE;
        break;
    }
    data->spec.rate = device->Frequency;
    data->spec.channels = ChannelsFromDevFmt(device->FmtChans);

    if(pa_sample_spec_valid(&data->spec) == 0)
    {
        ERR("Invalid sample format\n");
        pa_threaded_mainloop_unlock(data->loop);
        return ALC_FALSE;
    }

    if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
    {
        ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
        pa_threaded_mainloop_unlock(data->loop);
        return ALC_FALSE;
    }
    SetDefaultWFXChannelOrder(device);

    data->attr.fragsize = -1;
    data->attr.prebuf = 0;
    data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec);
    data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2);
    data->attr.maxlength = -1;

    data->stream = connect_playback_stream(data->device_name, data->loop,
                                           data->context, flags, &data->attr,
                                           &data->spec, &chanmap);
    if(!data->stream)
    {
        pa_threaded_mainloop_unlock(data->loop);
        return ALC_FALSE;
    }
    pa_stream_set_state_callback(data->stream, stream_state_callback2, device);

    data->spec = *(pa_stream_get_sample_spec(data->stream));
    if(device->Frequency != data->spec.rate)
    {
        pa_operation *o;

        /* Server updated our playback rate, so modify the buffer attribs
         * accordingly. */
        device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency *
                                      data->spec.rate + 0.5);
        data->attr.minreq  = device->UpdateSize * pa_frame_size(&data->spec);
        data->attr.tlength = data->attr.minreq * clampu(device->NumUpdates, 2, 16);
        data->attr.maxlength = -1;
        data->attr.prebuf  = 0;

        o = pa_stream_set_buffer_attr(data->stream, &data->attr,
                                      stream_success_callback, device);
        wait_for_operation(o, data->loop);

        device->Frequency = data->spec.rate;
    }

    pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);
#if PA_CHECK_VERSION(0,9,15)
    if(pa_stream_set_buffer_attr_callback)
        pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
#endif
    stream_buffer_attr_callback(data->stream, device);

    len = data->attr.minreq / pa_frame_size(&data->spec);
    if((CPUCapFlags&CPU_CAP_SSE))
        len = (len+3)&~3;
    device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5);
    device->NumUpdates = clampu(device->NumUpdates, 2, 16);
    device->UpdateSize = len;

    pa_threaded_mainloop_unlock(data->loop);
    return ALC_TRUE;
}
Beispiel #12
0
static int Open(vlc_object_t *obj)
{
    demux_t *demux = (demux_t *)obj;

    demux_sys_t *sys = malloc(sizeof (*sys));
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;

    sys->context = vlc_pa_connect(obj, &sys->mainloop);
    if (sys->context == NULL) {
        free(sys);
        return VLC_EGENERIC;
    }

    sys->stream = NULL;
    sys->es = NULL;
    sys->discontinuity = false;
    sys->caching = INT64_C(1000) * var_InheritInteger(obj, "live-caching");
    demux->p_sys = sys;

    /* Stream parameters */
    struct pa_sample_spec ss;
    ss.format = PA_SAMPLE_S16NE;
    ss.rate = 48000;
    ss.channels = 2;
    assert(pa_sample_spec_valid(&ss));

    struct pa_channel_map map;
    map.channels = 2;
    map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
    map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
    assert(pa_channel_map_valid(&map));

    const pa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING
                                  | PA_STREAM_AUTO_TIMING_UPDATE
                                  | PA_STREAM_ADJUST_LATENCY
                                  | PA_STREAM_FIX_FORMAT
                                  | PA_STREAM_FIX_RATE
                                  /*| PA_STREAM_FIX_CHANNELS*/;

    const char *dev = NULL;
    if (demux->psz_location != NULL && demux->psz_location[0] != '\0')
        dev = demux->psz_location;

    struct pa_buffer_attr attr = {
        .maxlength = -1,
        .fragsize = pa_usec_to_bytes(sys->caching, &ss) / 2,
    };

    es_format_t fmt;

    /* Create record stream */
    pa_stream *s;
    pa_operation *op;

    pa_threaded_mainloop_lock(sys->mainloop);
    s = pa_stream_new(sys->context, "audio stream", &ss, &map);
    if (s == NULL)
        goto error;

    sys->stream = s;
    pa_stream_set_state_callback(s, stream_state_cb, sys->mainloop);
    pa_stream_set_read_callback(s, stream_read_cb, demux);
    pa_stream_set_buffer_attr_callback(s, stream_buffer_attr_cb, demux);
    pa_stream_set_moved_callback(s, stream_moved_cb, demux);
    pa_stream_set_overflow_callback(s, stream_overflow_cb, demux);
    pa_stream_set_started_callback(s, stream_started_cb, demux);
    pa_stream_set_suspended_callback(s, stream_suspended_cb, demux);
    pa_stream_set_underflow_callback(s, stream_underflow_cb, demux);

    if (pa_stream_connect_record(s, dev, &attr, flags) < 0
     || stream_wait(s, sys->mainloop)) {
        vlc_pa_error(obj, "cannot connect record stream", sys->context);
        goto error;
    }

    /* The ES should be initialized before stream_read_cb(), but how? */
    const struct pa_sample_spec *pss = pa_stream_get_sample_spec(s);
    if ((unsigned)pss->format >= sizeof (fourccs) / sizeof (fourccs[0])) {
        msg_Err(obj, "unknown PulseAudio sample format %u",
                (unsigned)pss->format);
        goto error;
    }

    vlc_fourcc_t format = fourccs[pss->format];
    if (format == 0) { /* FIXME: should renegotiate something else */
        msg_Err(obj, "unsupported PulseAudio sample format %u",
                (unsigned)pss->format);
        goto error;
    }

    es_format_Init(&fmt, AUDIO_ES, format);
    fmt.audio.i_physical_channels = fmt.audio.i_original_channels =
        AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
    fmt.audio.i_channels = ss.channels;
    fmt.audio.i_rate = pss->rate;
    fmt.audio.i_bitspersample = aout_BitsPerSample(format);
    fmt.audio.i_blockalign = fmt.audio.i_bitspersample * ss.channels / 8;
    fmt.i_bitrate = fmt.audio.i_bitspersample * ss.channels * pss->rate;
    sys->framesize = fmt.audio.i_blockalign;
    sys->es = es_out_Add (demux->out, &fmt);

    /* Update the buffer attributes according to actual format */
    attr.fragsize = pa_usec_to_bytes(sys->caching, pss) / 2;
    op = pa_stream_set_buffer_attr(s, &attr, stream_success_cb, sys->mainloop);
    if (likely(op != NULL)) {
        while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
            pa_threaded_mainloop_wait(sys->mainloop);
        pa_operation_unref(op);
    }
    stream_buffer_attr_cb(s, demux);
    pa_threaded_mainloop_unlock(sys->mainloop);

    demux->pf_demux = NULL;
    demux->pf_control = Control;
    return VLC_SUCCESS;

error:
    pa_threaded_mainloop_unlock(sys->mainloop);
    Close(obj);
    return VLC_EGENERIC;
}

static void Close (vlc_object_t *obj)
{
    demux_t *demux = (demux_t *)obj;
    demux_sys_t *sys = demux->p_sys;
    pa_stream *s = sys->stream;

    if (likely(s != NULL)) {
        pa_threaded_mainloop_lock(sys->mainloop);
        pa_stream_disconnect(s);
        pa_stream_set_state_callback(s, NULL, NULL);
        pa_stream_set_read_callback(s, NULL, NULL);
        pa_stream_set_buffer_attr_callback(s, NULL, NULL);
        pa_stream_set_moved_callback(s, NULL, NULL);
        pa_stream_set_overflow_callback(s, NULL, NULL);
        pa_stream_set_started_callback(s, NULL, NULL);
        pa_stream_set_suspended_callback(s, NULL, NULL);
        pa_stream_set_underflow_callback(s, NULL, NULL);
        pa_stream_unref(s);
        pa_threaded_mainloop_unlock(sys->mainloop);
    }

    vlc_pa_disconnect(obj, sys->context, sys->mainloop);
    free(sys);
}