~paulliu/ubuntu/quantal/freerdp/fixext

« back to all changes in this revision

Viewing changes to channels/drdynvc/tsmf/pulse/tsmf_pulse.c

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2012-01-31 10:02:14 UTC
  • mto: This revision was merged to the branch mainline in revision 11.
  • Revision ID: package-import@ubuntu.com-20120131100214-zvig71djj2sqgq22
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * FreeRDP: A Remote Desktop Protocol client.
 
3
 * Video Redirection Virtual Channel - PulseAudio Device
 
4
 *
 
5
 * Copyright 2010-2011 Vic Lee
 
6
 *
 
7
 * Licensed under the Apache License, Version 2.0 (the "License");
 
8
 * you may not use this file except in compliance with the License.
 
9
 * You may obtain a copy of the License at
 
10
 *
 
11
 *     http://www.apache.org/licenses/LICENSE-2.0
 
12
 *
 
13
 * Unless required by applicable law or agreed to in writing, software
 
14
 * distributed under the License is distributed on an "AS IS" BASIS,
 
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
16
 * See the License for the specific language governing permissions and
 
17
 * limitations under the License.
 
18
 */
 
19
 
 
20
#include <stdio.h>
 
21
#include <stdlib.h>
 
22
#include <string.h>
 
23
#include <unistd.h>
 
24
#include <pulse/pulseaudio.h>
 
25
#include <freerdp/utils/memory.h>
 
26
 
 
27
#include "tsmf_audio.h"
 
28
 
 
29
typedef struct _TSMFPulseAudioDevice
 
30
{
 
31
        ITSMFAudioDevice iface;
 
32
 
 
33
        char device[32];
 
34
        pa_threaded_mainloop* mainloop;
 
35
        pa_context* context;
 
36
        pa_sample_spec sample_spec;
 
37
        pa_stream* stream;
 
38
} TSMFPulseAudioDevice;
 
39
 
 
40
static void tsmf_pulse_context_state_callback(pa_context* context, void* userdata)
 
41
{
 
42
        TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
 
43
        pa_context_state_t state;
 
44
 
 
45
        state = pa_context_get_state(context);
 
46
        switch (state)
 
47
        {
 
48
                case PA_CONTEXT_READY:
 
49
                        DEBUG_DVC("PA_CONTEXT_READY");
 
50
                        pa_threaded_mainloop_signal(pulse->mainloop, 0);
 
51
                        break;
 
52
 
 
53
                case PA_CONTEXT_FAILED:
 
54
                case PA_CONTEXT_TERMINATED:
 
55
                        DEBUG_DVC("state %d", (int)state);
 
56
                        pa_threaded_mainloop_signal(pulse->mainloop, 0);
 
57
                        break;
 
58
 
 
59
                default:
 
60
                        DEBUG_DVC("state %d", (int)state);
 
61
                        break;
 
62
        }
 
63
}
 
64
 
 
65
static boolean tsmf_pulse_connect(TSMFPulseAudioDevice* pulse)
 
66
{
 
67
        pa_context_state_t state;
 
68
 
 
69
        if (!pulse->context)
 
70
                return false;
 
71
 
 
72
        if (pa_context_connect(pulse->context, NULL, 0, NULL))
 
73
        {
 
74
                DEBUG_WARN("pa_context_connect failed (%d)",
 
75
                        pa_context_errno(pulse->context));
 
76
                return false;
 
77
        }
 
78
        pa_threaded_mainloop_lock(pulse->mainloop);
 
79
        if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
 
80
        {
 
81
                pa_threaded_mainloop_unlock(pulse->mainloop);
 
82
                DEBUG_WARN("pa_threaded_mainloop_start failed (%d)",
 
83
                        pa_context_errno(pulse->context));
 
84
                return false;
 
85
        }
 
86
        for (;;)
 
87
        {
 
88
                state = pa_context_get_state(pulse->context);
 
89
                if (state == PA_CONTEXT_READY)
 
90
                        break;
 
91
                if (!PA_CONTEXT_IS_GOOD(state))
 
92
                {
 
93
                        DEBUG_DVC("bad context state (%d)",
 
94
                                pa_context_errno(pulse->context));
 
95
                        break;
 
96
                }
 
97
                pa_threaded_mainloop_wait(pulse->mainloop);
 
98
        }
 
99
        pa_threaded_mainloop_unlock(pulse->mainloop);
 
100
        if (state == PA_CONTEXT_READY)
 
101
        {
 
102
                DEBUG_DVC("connected");
 
103
                return true;
 
104
        }
 
105
        else
 
106
        {
 
107
                pa_context_disconnect(pulse->context);
 
108
                return false;
 
109
        }
 
110
}
 
111
 
 
112
static boolean tsmf_pulse_open(ITSMFAudioDevice* audio, const char* device)
 
113
{
 
114
        TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
 
115
 
 
116
        if (device)
 
117
        {
 
118
                strcpy(pulse->device, device);
 
119
        }
 
120
 
 
121
        pulse->mainloop = pa_threaded_mainloop_new();
 
122
        if (!pulse->mainloop)
 
123
        {
 
124
                DEBUG_WARN("pa_threaded_mainloop_new failed");
 
125
                return false;
 
126
        }
 
127
        pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
 
128
        if (!pulse->context)
 
129
        {
 
130
                DEBUG_WARN("pa_context_new failed");
 
131
                return false;
 
132
        }
 
133
        pa_context_set_state_callback(pulse->context, tsmf_pulse_context_state_callback, pulse);
 
134
        if (tsmf_pulse_connect(pulse))
 
135
        {
 
136
                DEBUG_WARN("tsmf_pulse_connect failed");
 
137
                return false;
 
138
        }
 
139
 
 
140
        DEBUG_DVC("open device %s", pulse->device);
 
141
        return true;
 
142
}
 
143
 
 
144
static void tsmf_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
 
145
{
 
146
        TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
 
147
 
 
148
        pa_threaded_mainloop_signal(pulse->mainloop, 0);
 
149
}
 
150
 
 
151
static void tsmf_pulse_wait_for_operation(TSMFPulseAudioDevice* pulse, pa_operation* operation)
 
152
{
 
153
        if (operation == NULL)
 
154
                return;
 
155
        while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
 
156
        {
 
157
                pa_threaded_mainloop_wait(pulse->mainloop);
 
158
        }
 
159
        pa_operation_unref(operation);
 
160
}
 
161
 
 
162
static void tsmf_pulse_stream_state_callback(pa_stream* stream, void* userdata)
 
163
{
 
164
        TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
 
165
        pa_stream_state_t state;
 
166
 
 
167
        state = pa_stream_get_state(stream);
 
168
        switch (state)
 
169
        {
 
170
                case PA_STREAM_READY:
 
171
                        DEBUG_DVC("PA_STREAM_READY");
 
172
                        pa_threaded_mainloop_signal (pulse->mainloop, 0);
 
173
                        break;
 
174
 
 
175
                case PA_STREAM_FAILED:
 
176
                case PA_STREAM_TERMINATED:
 
177
                        DEBUG_DVC("state %d", (int)state);
 
178
                        pa_threaded_mainloop_signal (pulse->mainloop, 0);
 
179
                        break;
 
180
 
 
181
                default:
 
182
                        DEBUG_DVC("state %d", (int)state);
 
183
                        break;
 
184
        }
 
185
}
 
186
 
 
187
static void tsmf_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
 
188
{
 
189
        TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
 
190
 
 
191
        DEBUG_DVC("%d", (int) length);
 
192
 
 
193
        pa_threaded_mainloop_signal(pulse->mainloop, 0);
 
194
}
 
195
 
 
196
static boolean tsmf_pulse_close_stream(TSMFPulseAudioDevice* pulse)
 
197
{
 
198
        if (!pulse->context || !pulse->stream)
 
199
                return false;
 
200
 
 
201
        DEBUG_DVC("");
 
202
 
 
203
        pa_threaded_mainloop_lock(pulse->mainloop);
 
204
        pa_stream_set_write_callback(pulse->stream, NULL, NULL);
 
205
        tsmf_pulse_wait_for_operation(pulse,
 
206
                pa_stream_drain(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
 
207
        pa_stream_disconnect(pulse->stream);
 
208
        pa_stream_unref(pulse->stream);
 
209
        pulse->stream = NULL;
 
210
        pa_threaded_mainloop_unlock(pulse->mainloop);
 
211
 
 
212
        return true;
 
213
}
 
214
 
 
215
static boolean tsmf_pulse_open_stream(TSMFPulseAudioDevice* pulse)
 
216
{
 
217
        pa_stream_state_t state;
 
218
        pa_buffer_attr buffer_attr = { 0 };
 
219
 
 
220
        if (!pulse->context)
 
221
                return false;
 
222
 
 
223
        DEBUG_DVC("");
 
224
 
 
225
        pa_threaded_mainloop_lock(pulse->mainloop);
 
226
        pulse->stream = pa_stream_new(pulse->context, "freerdp",
 
227
                &pulse->sample_spec, NULL);
 
228
        if (!pulse->stream)
 
229
        {
 
230
                pa_threaded_mainloop_unlock(pulse->mainloop);
 
231
                DEBUG_WARN("pa_stream_new failed (%d)",
 
232
                        pa_context_errno(pulse->context));
 
233
                return false;
 
234
        }
 
235
        pa_stream_set_state_callback(pulse->stream,
 
236
                tsmf_pulse_stream_state_callback, pulse);
 
237
        pa_stream_set_write_callback(pulse->stream,
 
238
                tsmf_pulse_stream_request_callback, pulse);
 
239
        buffer_attr.maxlength = pa_usec_to_bytes(500000, &pulse->sample_spec);
 
240
        buffer_attr.tlength = pa_usec_to_bytes(250000, &pulse->sample_spec);
 
241
        buffer_attr.prebuf = (uint32_t) -1;
 
242
        buffer_attr.minreq = (uint32_t) -1;
 
243
        buffer_attr.fragsize = (uint32_t) -1;
 
244
        if (pa_stream_connect_playback(pulse->stream,
 
245
                pulse->device[0] ? pulse->device : NULL, &buffer_attr,
 
246
                PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
 
247
                NULL, NULL) < 0)
 
248
        {
 
249
                pa_threaded_mainloop_unlock(pulse->mainloop);
 
250
                DEBUG_WARN("pa_stream_connect_playback failed (%d)",
 
251
                        pa_context_errno(pulse->context));
 
252
                return false;
 
253
        }
 
254
 
 
255
        for (;;)
 
256
        {
 
257
                state = pa_stream_get_state(pulse->stream);
 
258
                if (state == PA_STREAM_READY)
 
259
                        break;
 
260
                if (!PA_STREAM_IS_GOOD(state))
 
261
                {
 
262
                        DEBUG_WARN("bad stream state (%d)",
 
263
                                pa_context_errno(pulse->context));
 
264
                        break;
 
265
                }
 
266
                pa_threaded_mainloop_wait(pulse->mainloop);
 
267
        }
 
268
        pa_threaded_mainloop_unlock(pulse->mainloop);
 
269
        if (state == PA_STREAM_READY)
 
270
        {
 
271
                DEBUG_DVC("connected");
 
272
                return true;
 
273
        }
 
274
        else
 
275
        {
 
276
                tsmf_pulse_close_stream(pulse);
 
277
                return false;
 
278
        }
 
279
}
 
280
 
 
281
static boolean tsmf_pulse_set_format(ITSMFAudioDevice* audio,
 
282
        uint32 sample_rate, uint32 channels, uint32 bits_per_sample)
 
283
{
 
284
        TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
 
285
 
 
286
        DEBUG_DVC("sample_rate %d channels %d bits_per_sample %d",
 
287
                sample_rate, channels, bits_per_sample);
 
288
 
 
289
        pulse->sample_spec.rate = sample_rate;
 
290
        pulse->sample_spec.channels = channels;
 
291
        pulse->sample_spec.format = PA_SAMPLE_S16LE;
 
292
 
 
293
        return tsmf_pulse_open_stream(pulse);
 
294
}
 
295
 
 
296
static boolean tsmf_pulse_play(ITSMFAudioDevice* audio, uint8* data, uint32 data_size)
 
297
{
 
298
        TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
 
299
        uint8* src;
 
300
        int len;
 
301
        int ret;
 
302
 
 
303
        DEBUG_DVC("data_size %d", data_size);
 
304
 
 
305
        if (pulse->stream)
 
306
        {
 
307
                pa_threaded_mainloop_lock(pulse->mainloop);
 
308
 
 
309
                src = data;
 
310
                while (data_size > 0)
 
311
                {
 
312
                        while ((len = pa_stream_writable_size(pulse->stream)) == 0)
 
313
                        {
 
314
                                DEBUG_DVC("waiting");
 
315
                                pa_threaded_mainloop_wait(pulse->mainloop);
 
316
                        }
 
317
                        if (len < 0)
 
318
                                break;
 
319
                        if (len > data_size)
 
320
                                len = data_size;
 
321
                        ret = pa_stream_write(pulse->stream, src, len, NULL, 0LL, PA_SEEK_RELATIVE);
 
322
                        if (ret < 0)
 
323
                        {
 
324
                                DEBUG_DVC("pa_stream_write failed (%d)",
 
325
                                        pa_context_errno(pulse->context));
 
326
                                break;
 
327
                        }
 
328
                        src += len;
 
329
                        data_size -= len;
 
330
                }
 
331
 
 
332
                pa_threaded_mainloop_unlock(pulse->mainloop);
 
333
        }
 
334
        xfree(data);
 
335
 
 
336
        return true;
 
337
}
 
338
 
 
339
static uint64 tsmf_pulse_get_latency(ITSMFAudioDevice* audio)
 
340
{
 
341
        pa_usec_t usec;
 
342
        uint64 latency = 0;
 
343
        TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
 
344
 
 
345
        if (pulse->stream && pa_stream_get_latency(pulse->stream, &usec, NULL) == 0)
 
346
        {
 
347
                latency = ((uint64)usec) * 10LL;
 
348
        }
 
349
        return latency;
 
350
}
 
351
 
 
352
static void tsmf_pulse_flush(ITSMFAudioDevice* audio)
 
353
{
 
354
        TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
 
355
 
 
356
        pa_threaded_mainloop_lock(pulse->mainloop);
 
357
        tsmf_pulse_wait_for_operation(pulse,
 
358
                pa_stream_flush(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
 
359
        pa_threaded_mainloop_unlock(pulse->mainloop);
 
360
}
 
361
 
 
362
static void tsmf_pulse_free(ITSMFAudioDevice* audio)
 
363
{
 
364
        TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
 
365
 
 
366
        DEBUG_DVC("");
 
367
 
 
368
        tsmf_pulse_close_stream(pulse);
 
369
        if (pulse->mainloop)
 
370
        {
 
371
                pa_threaded_mainloop_stop(pulse->mainloop);
 
372
        }
 
373
        if (pulse->context)
 
374
        {
 
375
                pa_context_disconnect(pulse->context);
 
376
                pa_context_unref(pulse->context);
 
377
                pulse->context = NULL;
 
378
        }
 
379
        if (pulse->mainloop)
 
380
        {
 
381
                pa_threaded_mainloop_free(pulse->mainloop);
 
382
                pulse->mainloop = NULL;
 
383
        }
 
384
        xfree(pulse);
 
385
}
 
386
 
 
387
ITSMFAudioDevice* TSMFAudioDeviceEntry(void)
 
388
{
 
389
        TSMFPulseAudioDevice* pulse;
 
390
 
 
391
        pulse = xnew(TSMFPulseAudioDevice);
 
392
 
 
393
        pulse->iface.Open = tsmf_pulse_open;
 
394
        pulse->iface.SetFormat = tsmf_pulse_set_format;
 
395
        pulse->iface.Play = tsmf_pulse_play;
 
396
        pulse->iface.GetLatency = tsmf_pulse_get_latency;
 
397
        pulse->iface.Flush = tsmf_pulse_flush;
 
398
        pulse->iface.Free = tsmf_pulse_free;
 
399
 
 
400
        return (ITSMFAudioDevice*) pulse;
 
401
}
 
402