source: roaraudio/libroarpulse/stream.c @ 3464:90b4012c0372

Last change on this file since 3464:90b4012c0372 was 3464:90b4012c0372, checked in by phi, 14 years ago

temp workaround

File size: 21.6 KB
Line 
1//stream.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010
5 *  The code (may) include prototypes and comments (and maybe
6 *  other code fragements) from libpulse*. They are mostly copyrighted by:
7 *  Lennart Poettering <poettering@users.sourceforge.net> and
8 *  Pierre Ossman <drzeus@drzeus.cx>
9 *
10 *  This file is part of libroarpulse a part of RoarAudio,
11 *  a cross-platform sound system for both, home and professional use.
12 *  See README for details.
13 *
14 *  This file is free software; you can redistribute it and/or modify
15 *  it under the terms of the GNU General Public License version 3
16 *  as published by the Free Software Foundation.
17 *
18 *  RoarAudio is distributed in the hope that it will be useful,
19 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 *  GNU General Public License for more details.
22 *
23 *  You should have received a copy of the GNU General Public License
24 *  along with this software; see the file COPYING.  If not, write to
25 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
26 *
27 *  NOTE for everyone want's to change something and send patches:
28 *  read README and HACKING! There a addition information on
29 *  the license of this document you need to read before you send
30 *  any patches.
31 *
32 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
33 *  or libpulse*:
34 *  The libs libroaresd, libroararts and libroarpulse link this libroar
35 *  and are therefore GPL. Because of this it may be illigal to use
36 *  them with any software that uses libesd, libartsc or libpulse*.
37 */
38
39#include <libroarpulse/libroarpulse.h>
40
41struct _roar_pa_stream_cb {
42 union {
43  pa_stream_notify_cb_t  ncb;
44  pa_stream_request_cb_t rcb;
45  pa_stream_success_cb_t scb;
46 } cb;
47 void * userdata;
48};
49
50struct pa_stream {
51 size_t                  refc;
52 pa_context            * c;
53 struct roar_vio_calls   vio;
54 struct roar_stream      stream;
55 pa_stream_state_t       state;
56 pa_sample_spec          sspec;
57 pa_io_event           * io_event;
58 pa_timing_info          timinginfo;
59 pa_buffer_attr          bufattr;
60 struct roar_buffer    * iobuffer;
61 struct {
62  size_t size;
63  size_t num;
64 } fragments;
65 struct {
66  struct _roar_pa_stream_cb change_state;
67  struct _roar_pa_stream_cb write;
68  struct _roar_pa_stream_cb read;
69  struct _roar_pa_stream_cb overflow;
70  struct _roar_pa_stream_cb underflow;
71  struct _roar_pa_stream_cb latency;
72  struct _roar_pa_stream_cb drain;
73 } cb;
74 struct {
75  pa_operation * drain;
76 } op;
77};
78
79typedef void pa_proplist;
80void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
81
82pa_stream* pa_stream_new_with_proplist(
83        pa_context *c                     ,
84        const char *name                  ,
85        const pa_sample_spec *ss          ,
86        const pa_channel_map *map         ,
87        pa_proplist *p                    );
88
89/** Create a new, unconnected stream with the specified name and sample type */
90pa_stream* pa_stream_new(
91        pa_context *c                     /**< The context to create this stream in */,
92        const char *name                  /**< A name for this stream */,
93        const pa_sample_spec *ss          /**< The desired sample format */,
94        const pa_channel_map *map         /**< The desired channel map, or NULL for default */) {
95 return pa_stream_new_with_proplist(c, name, ss, map, NULL);
96}
97
98pa_stream* pa_stream_new_with_proplist(
99        pa_context *c                     ,
100        const char *name                  ,
101        const pa_sample_spec *ss          ,
102        const pa_channel_map *map         ,
103        pa_proplist *p                    ) {
104 pa_stream * s;
105
106 ROAR_DBG("pa_stream_new_with_proplist(c=%p, name='%s', ss=%p, map=%p, p=%p) = ?", c, name, ss, map, p);
107
108 if ( p != NULL )
109  return NULL;
110
111 if ( (s = roar_mm_malloc(sizeof(pa_stream))) == NULL )
112  return NULL;
113
114 memset(s, 0, sizeof(pa_stream));
115
116 memcpy(&(s->sspec), ss, sizeof(pa_sample_spec));
117
118 ROAR_DBG("pa_stream_new_with_proplist(c=%p, name='%s', ss=%p, map=%p, p=%p) = ?", c, name, ss, map, p);
119
120 s->fragments.num  = 4;
121 s->fragments.size = 2048;
122
123 s->state = PA_STREAM_UNCONNECTED;
124 s->c     = c;
125 pa_context_ref(c);
126
127 ROAR_DBG("pa_stream_new_with_proplist(c=%p, name='%s', ss=%p, map=%p, p=%p) = ?", c, name, ss, map, p);
128
129 return s;
130}
131
132static void _pa_stream_free(pa_stream * s) {
133 pa_stream_disconnect(s);
134 pa_context_unref(s->c);
135 roar_mm_free(s);
136}
137
138/** Decrease the reference counter by one */
139void pa_stream_unref(pa_stream *s) {
140 if ( s == NULL )
141  return;
142
143 s->refc--;
144
145 if (s->refc < 1 )
146  _pa_stream_free(s);
147}
148
149/** Increase the reference counter by one */
150pa_stream *pa_stream_ref(pa_stream *s) {
151 if ( s == NULL )
152  return NULL;
153
154 s->refc++;
155
156 return s;
157}
158
159/** Return the current state of the stream */
160pa_stream_state_t pa_stream_get_state(pa_stream *p) {
161 if ( p == NULL )
162  return PA_STREAM_FAILED;
163
164 return p->state;
165}
166
167/** Return the context this stream is attached to */
168pa_context* pa_stream_get_context(pa_stream *p) {
169 if ( p == NULL )
170  return NULL;
171
172 return p->c;
173}
174
175/** Return the device (sink input or source output) index this stream is connected to */
176uint32_t pa_stream_get_index(pa_stream *s) {
177 return 0;
178}
179
180static void _roar_pa_stream_ioecb(pa_mainloop_api     * ea,
181                                  pa_io_event         * e,
182                                  int                   fd,
183                                  pa_io_event_flags_t   events,
184                                  void                * userdata) {
185 ROAR_DBG("_roar_pa_stream_ioecb(*) = (void)");
186}
187
188static int _roar_pa_stream_open (pa_stream *s,
189                                 const char *dev,
190                                 const pa_buffer_attr *attr,
191                                 pa_stream_flags_t flags,
192                                 pa_cvolume *volume,
193                                 pa_stream *sync_stream,
194                                 pa_stream_direction_t dir) {
195 struct roar_connection * con;
196 pa_mainloop_api * api;
197 pa_io_event_flags_t event_flags = PA_IO_EVENT_HANGUP;
198 int fh;
199 int ctl = -1;
200
201 ROAR_DBG("_roar_pa_stream_open(s=%p, dev='%s', attr=%p, flags=%i, volume=%p, sync_stream=%p, dir=%i) = ?", s, dev, attr, flags, volume, sync_stream, dir);
202
203 if ( s == NULL )
204  return -1;
205
206 volume = NULL;
207
208 if ( attr != NULL || flags != 0 || volume != NULL || sync_stream != NULL ) {
209  pa_stream_set_state(s, PA_STREAM_FAILED);
210  return -1;
211 }
212
213 if ( (con = roar_pa_context_get_con(s->c)) == NULL ) {
214  pa_stream_set_state(s, PA_STREAM_FAILED);
215  return -1;
216 }
217
218 switch (dir) {
219  case PA_STREAM_PLAYBACK:
220    s->stream.dir = ROAR_DIR_PLAY;
221    ctl           = ROAR_VIO_CTL_GET_SELECT_WRITE_FH;
222    event_flags  |= PA_IO_EVENT_OUTPUT;
223   break;
224  case PA_STREAM_RECORD:
225    s->stream.dir = ROAR_DIR_MONITOR;
226    ctl           = ROAR_VIO_CTL_GET_SELECT_READ_FH;
227    event_flags  |= PA_IO_EVENT_INPUT;
228   break;
229  default:
230    pa_stream_set_state(s, PA_STREAM_FAILED);
231    return -1;
232   break;
233 }
234
235 if ( roar_pa_sspec2auinfo(&(s->stream.info), &(s->sspec)) == -1 ) {
236  pa_stream_set_state(s, PA_STREAM_FAILED);
237  return -1;
238 }
239
240 if ( roar_vio_simple_new_stream_obj(&(s->vio), con, &(s->stream),
241                                     s->stream.info.rate, s->stream.info.channels,
242                                     s->stream.info.bits, s->stream.info.codec,
243                                     s->stream.dir) == -1 ) {
244  pa_stream_set_state(s, PA_STREAM_FAILED);
245  return -1;
246 }
247
248 api = roar_pa_context_get_api(s->c);
249
250 if ( api != NULL && api->io_new != NULL ) {
251  if ( roar_vio_ctl(&(s->vio), ctl, &fh) != -1 ) {
252   s->io_event = api->io_new(api, fh, event_flags, _roar_pa_stream_ioecb, s);
253  }
254 }
255
256 // TODO: update s->fragments.
257
258 s->bufattr.maxlength = s->fragments.size * s->fragments.num;
259 s->bufattr.tlength   = s->fragments.size;
260 s->bufattr.prebuf    = 0;
261 s->bufattr.minreq    = 1;
262 s->bufattr.fragsize  = s->fragments.size;
263
264 pa_stream_set_state(s, PA_STREAM_READY);
265
266 return 0;
267}
268
269/** Connect the stream to a sink */
270int pa_stream_connect_playback(
271        pa_stream *s                  /**< The stream to connect to a sink */,
272        const char *dev               /**< Name of the sink to connect to, or NULL for default */ ,
273        const pa_buffer_attr *attr    /**< Buffering attributes, or NULL for default */,
274        pa_stream_flags_t flags       /**< Additional flags, or 0 for default */,
275        pa_cvolume *volume            /**< Initial volume, or NULL for default */,
276        pa_stream *sync_stream        /**< Synchronize this stream with the specified one, or NULL for a standalone stream*/) {
277 return _roar_pa_stream_open(s, dev, attr, flags, volume, sync_stream, PA_STREAM_PLAYBACK);
278}
279
280/** Connect the stream to a source */
281int pa_stream_connect_record(
282        pa_stream *s                  /**< The stream to connect to a source */ ,
283        const char *dev               /**< Name of the source to connect to, or NULL for default */,
284        const pa_buffer_attr *attr    /**< Buffer attributes, or NULL for default */,
285        pa_stream_flags_t flags       /**< Additional flags, or 0 for default */) {
286 return _roar_pa_stream_open(s, dev, attr, flags, NULL, NULL, PA_STREAM_RECORD);
287}
288
289/** Disconnect a stream from a source/sink */
290int pa_stream_disconnect(pa_stream *s) {
291 pa_mainloop_api * api;
292
293 if ( s == NULL )
294  return -1;
295
296 if ( s->state != PA_STREAM_READY )
297  return -1;
298
299 if ( s->io_event != NULL ) {
300  api = roar_pa_context_get_api(s->c);
301
302  if ( api != NULL && api->io_free != NULL ) {
303   api->io_free(s->io_event);
304   s->io_event = NULL;
305  }
306 }
307
308 roar_vio_close(&(s->vio));
309
310 pa_stream_set_state(s, PA_STREAM_TERMINATED);
311
312 return 0;
313}
314
315/** Write some data to the server (for playback sinks), if free_cb is
316 * non-NULL this routine is called when all data has been written out
317 * and an internal reference to the specified data is kept, the data
318 * is not copied. If NULL, the data is copied into an internal
319 * buffer. The client my freely seek around in the output buffer. For
320 * most applications passing 0 and PA_SEEK_RELATIVE as arguments for
321 * offset and seek should be useful.*/
322int pa_stream_write(
323        pa_stream *p             /**< The stream to use */,
324        const void *data         /**< The data to write */,
325        size_t length            /**< The length of the data to write */,
326        pa_free_cb_t free_cb     /**< A cleanup routine for the data or NULL to request an internal copy */,
327        int64_t offset,          /**< Offset for seeking, must be 0 for upload streams */
328        pa_seek_mode_t seek      /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */) {
329 struct roar_buffer * buf;
330 void               * bufdata;
331
332 // TODO: implement seeking in output buffer
333
334 if ( p == NULL )
335  return -1;
336
337 if ( offset != 0 || seek != PA_SEEK_RELATIVE )
338  return -1;
339
340 if ( data == NULL ) {
341  if ( length == 0 ) {
342   if ( free_cb != NULL )
343    free_cb(NULL);
344
345   return 0;
346  } else {
347   return -1;
348  }
349 }
350
351 // seems we have a valid write from here.
352
353 if ( roar_buffer_new(&buf, length) == -1 ) {
354  if ( free_cb != NULL )
355   free_cb((void*)data);
356
357  return -1;
358 }
359
360 if ( roar_buffer_get_data(buf, &bufdata) == -1 ) {
361  if ( free_cb != NULL )
362   free_cb((void*)data);
363
364  return -1;
365 }
366
367 memcpy(bufdata, data, length);
368 if ( free_cb != NULL )
369  free_cb((void*)data);
370
371 if ( p->iobuffer == NULL ) {
372  p->iobuffer = buf;
373 } else {
374  if ( roar_buffer_add(p->iobuffer, buf) == -1 )
375   return -1;
376 }
377
378 return 0;
379}
380
381/** Read the next fragment from the buffer (for recording).
382 * data will point to the actual data and length will contain the size
383 * of the data in bytes (which can be less than a complete framgnet).
384 * Use pa_stream_drop() to actually remove the data from the
385 * buffer. If no data is available will return a NULL pointer  \since 0.8 */
386int pa_stream_peek(
387        pa_stream *p                 /**< The stream to use */,
388        const void **data            /**< Pointer to pointer that will point to data */,
389        size_t *length              /**< The length of the data read */) {
390 if ( data == NULL || length == NULL )
391  return -1;
392
393 *data   = NULL;
394 *length = 0;
395
396 if ( p == NULL )
397  return -1;
398
399 if ( p->iobuffer == NULL )
400  return 0;
401
402 if ( roar_buffer_get_len(p->iobuffer, length) == -1 ) {
403  *length = 0;
404  return -1;
405 }
406
407 if ( roar_buffer_get_data(p->iobuffer, (void**)data) == -1 ) {
408  *length = 0;
409  *data   = NULL;
410  return -1;
411 }
412
413 return 0;
414}
415
416/** Remove the current fragment on record streams. It is invalid to do this without first
417 * calling pa_stream_peek(). \since 0.8 */
418int pa_stream_drop(pa_stream *p) {
419 if ( p == NULL )
420  return -1;
421
422 if ( p->iobuffer == NULL )
423  return -1;
424
425 return roar_buffer_next(&(p->iobuffer));
426}
427
428/** Return the nember of bytes that may be written using pa_stream_write() */
429size_t pa_stream_writable_size(pa_stream *p) {
430 struct roar_buffer_stats stats;
431
432 if ( p == NULL )
433  return 0;
434
435 if ( p->iobuffer == NULL )
436  return 0;
437
438 if ( roar_buffer_ring_stats(p->iobuffer, &stats) == -1 )
439  return 0;
440
441 if ( stats.parts > p->fragments.num )
442  return 0;
443
444 return (p->fragments.num - stats.parts)*p->fragments.size;
445}
446
447/** Return the number of bytes that may be read using pa_stream_read() \since 0.8 */
448size_t pa_stream_readable_size(pa_stream *p) {
449 struct roar_buffer_stats stats;
450
451 if ( p == NULL )
452  return 0;
453
454 if ( p->iobuffer == NULL )
455  return 0;
456
457 if ( roar_buffer_ring_stats(p->iobuffer, &stats) == -1 )
458  return 0;
459
460 return stats.bytes;
461}
462
463/** Drain a playback stream. Use this for notification when the buffer is empty */
464pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
465 if ( s == NULL )
466  return NULL;
467
468 s->cb.drain.cb.scb   = cb;
469 s->cb.drain.userdata = userdata;
470
471 if ( s->op.drain == NULL ) {
472  s->op.drain = roar_pa_operation_new(PA_OPERATION_RUNNING);
473 }
474
475 pa_operation_ref(s->op.drain);
476
477 return s->op.drain;
478}
479
480/** Request a timing info structure update for a stream. Use
481 * pa_stream_get_timing_info() to get access to the raw timing data,
482 * or pa_stream_get_time() or pa_stream_get_latency() to get cleaned
483 * up values. */
484pa_operation* pa_stream_update_timing_info(pa_stream *p, pa_stream_success_cb_t cb, void *userdata) {
485 int suc = 1;
486
487 if ( p == NULL )
488  return NULL;
489
490 if ( roar_get_stream(roar_pa_context_get_con(p->c), &(p->stream), p->stream.id) == -1 ) {
491  suc = 0;
492 }
493
494 // p->timinginfo
495 pa_gettimeofday(&(p->timinginfo.timestamp)); // we should interpolate between time before call and after
496
497 p->timinginfo.synchronized_clocks    = 0;
498 p->timinginfo.sink_usec              = 0;
499 p->timinginfo.source_usec            = 0;
500 p->timinginfo.transport_usec         = 0;
501 p->timinginfo.playing                = p->iobuffer != NULL;
502 p->timinginfo.write_index_corrupt    = 1;
503 p->timinginfo.write_index            = p->stream.pos * pa_frame_size(&(p->sspec));
504 p->timinginfo.read_index_corrupt     = 1;
505 p->timinginfo.read_index             = p->stream.pos * pa_frame_size(&(p->sspec));
506#if 0 /* newer versions */
507 p->timinginfo.configured_sink_usec   = p->timinginfo.sink_usec;
508 p->timinginfo.configured_source_usec = p->timinginfo.source_usec;
509 p->timinginfo.since_underrun         = 0;
510#endif
511
512 if ( cb != NULL ) {
513  cb(p, suc, userdata);
514 }
515
516 return roar_pa_op_new_done();
517}
518
519/** Set the callback function that is called whenever the state of the stream changes */
520void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
521 ROAR_DBG("pa_stream_set_state_callback(s=%p, cb=%p, userdata=%p) = ?", s, cb, userdata);
522
523 if ( s == NULL )
524  return;
525
526 s->cb.change_state.cb.ncb    = cb;
527 s->cb.change_state.userdata  = userdata;
528}
529
530void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
531 if ( s == NULL )
532  return;
533
534 ROAR_DBG("pa_stream_set_state(s=%p, st=%i): State: %i->%i", s, st, s->state, st);
535
536 s->state = st;
537
538 if ( s->cb.change_state.cb.ncb != NULL ) {
539  ROAR_DBG("pa_stream_set_state(s=%p, st=%i): calling callback at %p", s, st, s->cb.change_state.cb.ncb);
540  s->cb.change_state.cb.ncb(s, s->cb.change_state.userdata);
541 }
542 ROAR_DBG("pa_stream_set_state(s=%p, st=%i) = (void)", s, st);
543}
544
545/** Set the callback function that is called when new data may be
546 * written to the stream. */
547void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata) {
548 if ( p == NULL )
549  return;
550
551 p->cb.write.cb.rcb    = cb;
552 p->cb.write.userdata  = userdata;
553}
554
555/** Set the callback function that is called when new data is available from the stream.
556 * Return the number of bytes read. \since 0.8 */
557void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata) {
558 if ( p == NULL )
559  return;
560
561 p->cb.read.cb.rcb    = cb;
562 p->cb.read.userdata  = userdata;
563}
564
565/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) \since 0.8 */
566void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata) {
567 if ( p == NULL )
568  return;
569
570 p->cb.overflow.cb.ncb    = cb;
571 p->cb.overflow.userdata  = userdata;
572}
573
574/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) \since 0.8 */
575void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata) {
576 if ( p == NULL )
577  return;
578
579 p->cb.underflow.cb.ncb    = cb;
580 p->cb.underflow.userdata  = userdata;
581}
582
583/** Set the callback function that is called whenever a latency information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE streams only. (Only for playback streams) \since 0.8.2 */
584void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata) {
585 if ( p == NULL )
586  return;
587
588 p->cb.latency.cb.ncb    = cb;
589 p->cb.latency.userdata  = userdata;
590}
591
592/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */
593pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
594
595/** Flush the playback buffer of this stream. Most of the time you're
596 * better off using the parameter delta of pa_stream_write() instead of this
597 * function. Available on both playback and recording streams. \since 0.3 */
598pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
599 return pa_stream_drain(s, cb, userdata); // where is the differance to drain?
600}
601/** Reenable prebuffering as specified in the pa_buffer_attr
602 * structure. Available for playback streams only. \since 0.6 */
603pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
604
605/** Request immediate start of playback on this stream. This disables
606 * prebuffering as specified in the pa_buffer_attr
607 * structure, temporarily. Available for playback streams only. \since 0.3 */
608pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
609
610/** Rename the stream. \since 0.5 */
611pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
612
613/** Return the current playback/recording time. This is based on the
614 * data in the timing info structure returned by
615 * pa_stream_get_timing_info(). This function will usually only return
616 * new data if a timing info update has been recieved. Only if timing
617 * interpolation has been requested (PA_STREAM_INTERPOLATE_TIMING)
618 * the data from the last timing update is used for an estimation of
619 * the current playback/recording time based on the local time that
620 * passed since the timing info structure has been acquired. The time
621 * value returned by this function is guaranteed to increase
622 * monotonically. (that means: the returned value is always greater or
623 * equal to the value returned on the last call) This behaviour can
624 * be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be
625 * desirable to deal better with bad estimations of transport
626 * latencies, but may have strange effects if the application is not
627 * able to deal with time going 'backwards'. \since 0.6 */
628int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
629 if ( s == NULL || r_usec == NULL )
630  return -1;
631
632 *r_usec = s->stream.pos * 1000000 / s->stream.info.rate / s->stream.info.channels;
633
634 return 0;
635}
636
637/** Return the total stream latency. This function is based on
638 * pa_stream_get_time(). In case the stream is a monitoring stream the
639 * result can be negative, i.e. the captured samples are not yet
640 * played. In this case *negative is set to 1. \since 0.6 */
641int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
642 // TODO: Fix this:
643 // sinks: lateny of stream, mixer, output...
644 // sources: mixer, output, negative
645
646 if ( r_usec != NULL )
647  *r_usec = 0;
648
649 if ( negative != NULL )
650  *negative = 0;
651
652 return 0;
653}
654
655/** Return the latest raw timing data structure. The returned pointer
656 * points to an internal read-only instance of the timing
657 * structure. The user should make a copy of this structure if he
658 * wants to modify it. An in-place update to this data structure may
659 * be requested using pa_stream_update_timing_info(). If no
660 * pa_stream_update_timing_info() call was issued before, this
661 * function will fail with PA_ERR_NODATA. Please note that the
662 * write_index member field (and only this field) is updated on each
663 * pa_stream_write() call, not just when a timing update has been
664 * recieved. \since 0.8 */
665const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) {
666 if ( s == NULL )
667  return NULL;
668
669 return &(s->timinginfo);
670}
671
672/** Return a pointer to the stream's sample specification. \since 0.6 */
673const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {
674 if ( s == NULL )
675  return NULL;
676
677 return &(s->sspec);
678}
679
680/** Return a pointer to the stream's channel map. \since 0.8 */
681const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
682
683/** Return the buffer metrics of the stream. Only valid after the
684 * stream has been connected successfuly and if the server is at least
685 * PulseAudio 0.9. \since 0.9.0 */
686const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
687 if ( s == NULL )
688  return NULL;
689
690 return &(s->bufattr);
691}
692
693//ll
Note: See TracBrowser for help on using the repository browser.