source: roaraudio/roard/streams.c @ 634:01d26682dd80

Last change on this file since 634:01d26682dd80 was 634:01d26682dd80, checked in by phi, 16 years ago

allow setting -1 as fh for stream, added ROAR_DIR_OUTPUT where ROAR_DIR_MONITOR is in the code

File size: 18.2 KB
Line 
1//streams.c:
2
3#include "roard.h"
4
5int streams_init (void) {
6 int i;
7
8 for (i = 0; i < ROAR_STREAMS_MAX; i++)
9  g_streams[i] = NULL;
10
11 return 0;
12}
13
14int streams_free (void) {
15 int i;
16
17 for (i = 0; i < ROAR_STREAMS_MAX; i++) {
18  if ( g_streams[i] != NULL ) {
19   streams_delete(i);
20  }
21 }
22
23 return 0;
24}
25
26
27int streams_new    (void) {
28 int i, j;
29 struct roar_stream        * n = NULL;
30 struct roar_stream_server * s = NULL;
31
32 for (i = 0; i < ROAR_STREAMS_MAX; i++) {
33  if ( g_streams[i] == NULL ) {
34   s = ROAR_STREAM_SERVER(n = ROAR_STREAM(malloc(sizeof(struct roar_stream_server))));
35   if ( n == NULL ) {
36    ROAR_ERR("streams_new(void): can not allocate memory for new stream: %s", strerror(errno));
37    ROAR_DBG("streams_new(void) = -1");
38    return -1;
39   }
40
41   n->id         = i;
42   n->fh         = -1;
43//   n->pos_rel_id = i;
44   n->database   = NULL;
45   n->dataoff    = NULL;
46   n->datalen    = 0;
47   n->offset     = 0;
48   n->pos        = 0;
49
50   s->client          = -1;
51   s->socktype        = ROAR_SOCKET_TYPE_UNKNOWN;
52   s->buffer          = NULL;
53   s->need_extra      =  0;
54   s->output          = NULL;
55   s->is_new          =  1;
56   s->codecfilter     = -1;
57   s->pre_underruns   =  0;
58   s->post_underruns  =  0;
59   s->codec_orgi      = -1;
60
61   s->mixer.scale     = 65535;
62   s->mixer.rpg_mul   = 1;
63   s->mixer.rpg_div   = 1;
64   for (j = 0; j < ROAR_MAX_CHANNELS; j++)
65    s->mixer.mixer[j] = 65535;
66
67   for (j = 0; j < ROAR_META_MAX_PER_STREAM; j++) {
68    s->meta[j].type   = ROAR_META_TYPE_NONE;
69    s->meta[j].key[0] = 0;
70    s->meta[j].value  = NULL;
71   }
72
73   roar_vio_init_calls(&(s->vio));
74
75   g_streams[i] = s;
76   ROAR_DBG("streams_new(void): n->id=%i", n->id);
77   ROAR_DBG("streams_new(void) = %i", i);
78   return i;
79  }
80 }
81
82 return -1;
83}
84
85int streams_delete (int id) {
86 if ( g_streams[id] == NULL )
87  return 0;
88
89 ROAR_DBG("streams_delete(id=%i) = ?", id);
90 ROAR_DBG("streams_delete(id=%i): g_streams[id]->id=%i", id, ROAR_STREAM(g_streams[id])->id);
91
92 if ( g_streams[id]->codecfilter != -1 ) {
93  codecfilter_close(g_streams[id]->codecfilter_inst, g_streams[id]->codecfilter);
94  g_streams[id]->codecfilter_inst = NULL;
95  g_streams[id]->codecfilter = -1;
96 }
97
98 if ( g_streams[id]->client != -1 ) {
99  ROAR_DBG("streams_delete(id=%i): Stream is owned by client %i", id, g_streams[id]->client);
100  client_stream_delete(g_streams[id]->client, id);
101 }
102
103 if ( g_streams[id]->buffer != NULL )
104  roar_buffer_free(g_streams[id]->buffer);
105
106 if ( g_streams[id]->output != NULL )
107  free(g_streams[id]->output);
108
109 if ( ROAR_STREAM(g_streams[id])->fh != -1 )
110  close(ROAR_STREAM(g_streams[id])->fh);
111
112 free(g_streams[id]);
113
114 g_streams[id] = NULL;
115
116 ROAR_DBG("streams_delete(id=%i) = 0", id);
117 return 0;
118}
119
120int streams_set_client (int id, int client) {
121 if ( g_streams[id] == NULL )
122  return -1;
123
124 ROAR_DBG("streams_set_client(id=%i): g_streams[id]->id=%i", id, ROAR_STREAM(g_streams[id])->id);
125 g_streams[id]->client = client;
126
127 return 0;
128}
129
130int streams_set_fh     (int id, int fh) {
131 int dir;
132
133 if ( g_streams[id] == NULL )
134  return -1;
135
136 ROAR_DBG("streams_set_fh(id=%i): g_streams[id]->id=%i", id, ROAR_STREAM(g_streams[id])->id);
137
138 ROAR_STREAM(g_streams[id])->fh = fh;
139
140 if ( codecfilter_open(&(g_streams[id]->codecfilter_inst), &(g_streams[id]->codecfilter), NULL,
141                  ROAR_STREAM(g_streams[id])->info.codec, g_streams[id]) == -1 ) {
142  return streams_delete(id);
143 }
144
145 if ( fh == -1 ) { // yes, this is valid, indecats full vio!
146  return 0;
147 }
148
149 dir = ROAR_STREAM(g_streams[id])->dir;
150
151 if ( dir == ROAR_DIR_MONITOR || dir == ROAR_DIR_RECORD || dir == ROAR_DIR_OUTPUT ) {
152  shutdown(fh, SHUT_RD);
153 }
154
155 if ( dir == ROAR_DIR_FILTER ) {
156  return 0;
157 } else {
158  return roar_socket_nonblock(fh, ROAR_SOCKET_NONBLOCK);
159 }
160}
161
162int streams_get_fh     (int id) {
163 if ( id < 0 )
164  return -1;
165
166 if ( g_streams[id] == NULL )
167  return -1;
168
169 return ROAR_STREAM(g_streams[id])->fh;
170}
171
172int streams_get    (int id, struct roar_stream_server ** stream) {
173 if ( g_streams[id] == NULL )
174  return -1;
175
176 *stream = g_streams[id];
177
178 return 0;
179}
180
181int streams_set_socktype (int id, int socktype) {
182 if ( g_streams[id] == NULL )
183  return -1;
184
185 g_streams[id]->socktype = socktype;
186
187 return 0;
188}
189
190int streams_get_socktype (int id) {
191 if ( g_streams[id] == NULL )
192  return -1;
193
194 return g_streams[id]->socktype;
195}
196
197int streams_get_outputbuffer  (int id, void ** buffer, size_t size) {
198 if ( g_streams[id] == NULL )
199  return -1;
200
201 // output buffer size does never change.
202 if ( g_streams[id]->output != NULL ) {
203  *buffer = g_streams[id]->output;
204  return 0;
205 }
206
207 if ( (g_streams[id]->output = malloc(size)) == NULL ) {
208  ROAR_ERR("streams_get_outputbuffer(*): Can not alloc: %s", strerror(errno));
209  return -1;
210 }
211
212 *buffer = g_streams[id]->output;
213
214 return 0;
215}
216
217int streams_fill_mixbuffer (int id, struct roar_audio_info * info) {
218 // TODO: decide: is this the most complex, hacked, un-understadable,
219 //               un-writeable and even worse: un-readable
220 //               function in the whole project?
221 size_t todo = ROAR_OUTPUT_CALC_OUTBUFSIZE(info);
222 size_t needed = todo;
223 size_t todo_in;
224 size_t len, outlen;
225 size_t mul = 1, div = 1;
226 void * rest = NULL;
227 void * in   = NULL;
228 struct roar_buffer     * buf;
229 struct roar_audio_info * stream_info;
230 struct roar_stream_server * stream = g_streams[id];
231 int is_the_same = 0;
232
233 if ( g_streams[id] == NULL )
234  return -1;
235
236 if ( streams_get_outputbuffer(id, &rest, todo) == -1 ) {
237  return -1;
238 }
239
240 if ( rest == NULL ) {
241  return -1;
242 }
243
244 // set up stream_info
245
246 stream_info = &(ROAR_STREAM(stream)->info);
247
248 // calc todo_in
249 todo_in = ROAR_OUTPUT_CALC_OUTBUFSIZE(stream_info);
250
251 // calc mul and div:
252 mul = todo    / todo_in;
253 div = todo_in / todo;
254
255 if ( mul == 0 ) {
256  mul = 1;
257 } else {
258  div = 1;
259 }
260
261 ROAR_DBG("streams_fill_mixbuffer(*): mul=%i, div=%i", mul, div);
262
263 ROAR_DBG("streams_fill_mixbuffer(*): rest=%p, todo=%i->%i (in->out)", rest, todo_in, todo);
264 // are both (input and output) of same format?
265
266
267 ROAR_DBG("streams_fill_mixbuffer(*): stream_info:");
268 roar_debug_audio_info_print(stream_info);
269 ROAR_DBG("streams_fill_mixbuffer(*): info:");
270 roar_debug_audio_info_print(info);
271
272 is_the_same = stream_info->rate     == info->rate     && stream_info->bits  == info->bits &&
273               stream_info->channels == info->channels && stream_info->codec == info->codec;
274
275 ROAR_DBG("streams_fill_mixbuffer(*): is_the_same=%i", is_the_same);
276
277/* How it works:
278 *
279 * set a counter to the number of samples we need.
280 * loop until we have all samples done or no samples are
281 * left in our input buffer.
282 * If there a no samples left in the input buffer: fill the rest
283 * of the output buffer with zeros.
284 *
285 * The loop:
286 * get a buffer from the input.
287 * if it's bigger than needed, set an offset.
288 * The rest of the data:
289 * 0) convert endianness (codec) from remote to local...
290 * 1) change bits in of the samples
291 * 2) change sample rate
292 * 3) change the nummber of channels
293 * 4) insert into output buffer
294 */
295
296/*
297 // get our first buffer:
298
299 if ( stream_shift_buffer(id, &buf) == -1 ) {
300  return -1;
301 }
302
303 // first test for some basic simple cases...
304
305 if ( buf == NULL ) { // we habe nothing in input queue
306                      // we may memset() our output buffer OR
307                      // just return with -1 so we are going to
308                      // be ignored.
309  return -1;
310 }
311*/
312
313 while (todo) { // main loop
314  ROAR_DBG("streams_fill_mixbuffer(*): looping...");
315  // exit loop if nothing is left, even if we need more data..
316  if ( stream_shift_buffer(id, &buf) == -1 )
317   break;
318  if ( buf == NULL )
319   break;
320
321  // read the data for this loop...
322  roar_buffer_get_data(buf, &in);
323  roar_buffer_get_len(buf, &len);
324
325  ROAR_DBG("streams_fill_mixbuffer(*): len = %i", len);
326
327  if ( len > todo_in ) {
328   roar_buffer_set_offset(buf, todo_in);
329   len = todo_in;
330  } else {
331   roar_buffer_set_len(buf, 0); // queue for deletation
332  }
333
334  // we now have 'len' bytes in 'in'
335
336  // calc how much outlen this has...
337  outlen = (len * mul) / div;
338
339  ROAR_DBG("streams_fill_mixbuffer(*): outlen = %i, buf = %p, len = %i", outlen, in, len);
340
341  if ( is_the_same ) {
342/*
343 * 0) convert endianness (codec) from remote to local...
344 * 1) change bits in of the samples
345 * 2) change sample rate
346 * 3) change the nummber of channels
347   \\==> skiping,...
348 */
349   // * 4) insert into output buffer
350   ROAR_DBG("streams_fill_mixbuffer(*): memcpy: in->rest: %p -> %p", in, rest);
351   if ( memcpy(rest, in, len) != rest ) {
352    ROAR_ERR("streams_fill_mixbuffer(*): memcpy returned invalid pointer.");
353   }
354
355  } else {
356
357/*
358   // * 0) convert endianness (codec) from remote to local...
359   if ( stream_info->codec != info->codec ) {
360    // we neet to convert...
361    return -1;
362   }
363
364   // * 1) change bits in of the samples
365   if ( stream_info->bits != info->bits ) {
366    return -1;
367   }
368
369   // * 2) change sample rate
370   if ( stream_info->rate != info->rate ) {
371    return -1;
372   }
373
374   // * 3) change the nummber of channels
375   if ( stream_info->channels != info->channels ) {
376    return -1;
377   }
378
379   // * 4) insert into output buffer
380*/
381  // hey! we have roar_conv() :)
382
383  if ( roar_conv(rest, in, 8*len / stream_info->bits, stream_info, info) == -1 )
384   return -1;
385  }
386
387  if ( change_vol(rest, info->bits, rest, 8*outlen / info->bits, info->channels, &(stream->mixer)) == -1 )
388   return -1;
389
390  // we habe outlen bytes more...
391  todo    -= outlen;
392  rest    += outlen;
393  todo_in -= len;
394
395  roar_buffer_get_len(buf, &len);
396  ROAR_DBG("streams_fill_mixbuffer(*): New length of buffer %p is %i", buf, len);
397  if ( len == 0 ) {
398   roar_buffer_delete(buf, NULL);
399  } else {
400   stream_unshift_buffer(id, buf);
401  }
402 }
403
404//len = 0;
405//roar_buffer_get_len(buf, &len);
406
407/*
408 if ( len > 0 ) // we still have some data in this buffer, re-inserting it to the input buffers...
409  stream_unshift_buffer(id, buf);
410 else
411  buffer_delete(buf, NULL);
412*/
413
414 ROAR_STREAM(g_streams[id])->pos =
415      ROAR_MATH_OVERFLOW_ADD(ROAR_STREAM(g_streams[id])->pos,
416          ROAR_OUTPUT_CALC_OUTBUFSAMP(info, needed-todo));
417 //ROAR_WARN("stream=%i, pos=%u", id, ((struct roar_stream*)g_streams[id])->pos);
418
419 if ( todo > 0 ) { // zeroize the rest of the buffer
420  memset(rest, 0, todo);
421
422  if ( todo != ROAR_OUTPUT_CALC_OUTBUFSIZE(info) ) {
423   if ( g_streams[id]->is_new ) {
424    stream->pre_underruns++;
425   } else {
426    ROAR_WARN("streams_fill_mixbuffer(*): Underrun in stream: %i bytes missing, filling with zeros", todo);
427    stream->post_underruns++;
428   }
429
430   stream->is_new = 0;
431  }
432 } else {
433  stream->is_new = 0;
434 }
435
436 return 0;
437}
438
439
440int streams_get_mixbuffers (void *** bufferlist, struct roar_audio_info * info, unsigned int pos) {
441 static void * bufs[ROAR_STREAMS_MAX+1];
442 int i;
443 int have = 0;
444
445 for (i = 0; i < ROAR_STREAMS_MAX; i++) {
446  if ( g_streams[i] != NULL ) {
447   if ( ROAR_STREAM(g_streams[i])->dir != ROAR_DIR_PLAY )
448    continue;
449
450   if ( streams_get_outputbuffer(i, &bufs[have], ROAR_OUTPUT_CALC_OUTBUFSIZE(info)) == -1 ) {
451    ROAR_ERR("streams_get_mixbuffer(*): Can not alloc output buffer for stream %i, BAD!", i);
452    ROAR_ERR("streams_get_mixbuffer(*): Ignoring stream for this round.");
453    continue;
454   }
455   if ( streams_fill_mixbuffer(i, info) == -1 ) {
456    ROAR_ERR("streams_get_mixbuffer(*): Can not fill output buffer for stream %i, this should not happen", i);
457    continue;
458   }
459
460//   printf("D: bufs[have=%i] = %p\n", have, bufs[have]);
461
462   ROAR_DBG("streams_get_mixbuffers(*):  bufs[have] = %p", bufs[have]);
463   ROAR_DBG("streams_get_mixbuffers(*): *bufs[have] = 0x%08x...", *(uint32_t*)bufs[have]);
464
465   have++; // we have a new stream!
466  }
467 }
468
469 bufs[have] = NULL;
470 //printf("D: bufs[have=%i] = %p\n", have, bufs[have]);
471
472 ROAR_DBG("streams_get_mixbuffers(*): have = %i", have);
473
474 *bufferlist = bufs;
475 return have;
476}
477
478
479int stream_add_buffer  (int id, struct roar_buffer * buf) {
480 ROAR_DBG("stream_add_buffer(id=%i, buf=%p) = ?", id, buf);
481
482 if ( g_streams[id] == NULL )
483  return -1;
484
485 if ( g_streams[id]->buffer == NULL ) {
486  g_streams[id]->buffer = buf;
487  ROAR_DBG("stream_add_buffer(id=%i, buf=%p) = 0", id, buf);
488  return 0;
489 }
490
491 ROAR_DBG("stream_add_buffer(id=%i, buf=%p) = ?", id, buf);
492 return roar_buffer_add(g_streams[id]->buffer, buf);
493}
494
495int stream_shift_buffer   (int id, struct roar_buffer ** buf) {
496 struct roar_buffer * next;
497
498 if ( g_streams[id] == NULL )
499  return -1;
500
501 if ( g_streams[id]->buffer == NULL ) {
502  *buf = NULL;
503  return 0;
504 }
505
506 roar_buffer_get_next(g_streams[id]->buffer, &next);
507
508 *buf                  = g_streams[id]->buffer;
509 g_streams[id]->buffer = next;
510
511 return 0;
512}
513int stream_unshift_buffer (int id, struct roar_buffer *  buf) {
514 if ( g_streams[id] == NULL )
515  return -1;
516
517 if ( g_streams[id]->buffer == NULL ) {
518  g_streams[id]->buffer = buf;
519  return 0;
520 }
521
522 buf->next = NULL;
523
524 roar_buffer_add(buf, g_streams[id]->buffer);
525
526 g_streams[id]->buffer = buf;
527
528 return 0;
529}
530
531int streams_check  (int id) {
532 int fh;
533 ssize_t req, realreq, done;
534 struct roar_stream        *   s;
535 struct roar_stream_server *  ss;
536 struct roar_buffer        *   b;
537 char                      * buf;
538
539 if ( g_streams[id] == NULL )
540  return -1;
541
542 ROAR_DBG("streams_check(id=%i) = ?", id);
543
544 s = ROAR_STREAM(ss = g_streams[id]);
545
546 if ( (fh = s->fh) == -1 )
547  return 0;
548
549 if ( s->dir != ROAR_DIR_PLAY )
550  return 0;
551
552 ROAR_DBG("streams_check(id=%i): fh = %i", id, fh);
553
554 req  = ROAR_OUTPUT_BUFFER_SAMPLES * s->info.channels * s->info.bits / 8; // optimal size
555 req += ss->need_extra; // bytes left we sould get....
556
557 if ( roar_buffer_new(&b, req) == -1 ) {
558  ROAR_ERR("streams_check(*): Can not alloc buffer space!");
559  ROAR_DBG("streams_check(*) = -1");
560  return -1;
561 }
562
563 roar_buffer_get_data(b, (void **)&buf);
564
565 ROAR_DBG("streams_check(id=%i): buffer is up and ready ;)", id);
566
567 if ( ss->codecfilter == -1 ) {
568  realreq = req;
569/*
570  req = read(fh, buf, req);
571  if ( req < realreq ) { // we can do this as the stream is in nonblocking mode!
572   if ( (realreq = read(fh, buf+req, realreq-req)) > 0 )
573    req += realreq;
574  }
575*/
576  done = 0;
577  while (req > 0 && done != realreq) {
578   if ( (req = read(fh, buf+done, realreq-done)) > 0 )
579    done += req;
580  }
581  req = done;
582 } else {
583  req = codecfilter_read(ss->codecfilter_inst, ss->codecfilter, buf, req);
584 }
585
586 if ( req > 0 ) {
587  ROAR_DBG("streams_check(id=%i): got %i bytes", id, req);
588
589  roar_buffer_set_len(b, req);
590
591  if ( stream_add_buffer(id, b) != -1 )
592   return 0;
593
594  ROAR_ERR("streams_check(id=%i): something is wrong, could not add buffer to stream!", id);
595  roar_buffer_free(b);
596 } else {
597  ROAR_DBG("streams_check(id=%i): read() = %i // errno: %s", id, req, strerror(errno));
598#ifdef ROAR_HAVE_LIBVORBISFILE
599  if ( errno != EAGAIN && errno != ESPIPE ) { // libvorbis file trys to seek a bit ofen :)
600#else
601  if ( errno != EAGAIN ) {
602#endif
603   ROAR_DBG("streams_check(id=%i): EOF!", id);
604   streams_delete(id);
605   ROAR_DBG("streams_check(id=%i) = 0", id);
606  }
607  roar_buffer_free(b);
608  return 0;
609 }
610
611
612 ROAR_DBG("streams_check(id=%i) = -1", id);
613 return -1;
614}
615
616
617int streams_send_mon   (int id) {
618 int fh;
619 struct roar_stream        *   s;
620 struct roar_stream_server *  ss;
621 void * obuf;
622 int    olen;
623 int    need_to_free = 0;
624
625 if ( g_streams[id] == NULL )
626  return -1;
627
628 ROAR_DBG("streams_send_mon(id=%i) = ?", id);
629
630 s = ROAR_STREAM((ss = g_streams[id]));
631
632 if ( (fh = s->fh) == -1 )
633  return 0;
634
635 if ( s->dir != ROAR_DIR_MONITOR && s->dir != ROAR_DIR_OUTPUT )
636  return 0;
637
638 ROAR_DBG("streams_send_mon(id=%i): fh = %i", id, fh);
639
640 if ( s->info.channels != g_sa->channels || s->info.bits  != g_sa->bits ||
641      s->info.rate     != g_sa->rate     || s->info.codec != g_sa->codec  ) {
642  olen = ROAR_OUTPUT_CALC_OUTBUFSIZE(&(s->info)); // we hope g_output_buffer_len
643                                                  // is ROAR_OUTPUT_CALC_OUTBUFSIZE(g_sa) here
644  if ( (obuf = malloc(olen)) == NULL )
645   return -1;
646
647  need_to_free = 1;
648
649  ROAR_DBG("streams_send_mon(id=%i): obuf=%p, olen=%i", id, obuf, olen);
650
651  if ( roar_conv(obuf, g_output_buffer, ROAR_OUTPUT_BUFFER_SAMPLES*g_sa->channels, g_sa, &(s->info)) == -1 ) {
652   free(obuf);
653   return -1;
654  }
655 } else {
656  obuf = g_output_buffer;
657  olen = g_output_buffer_len;
658 }
659
660 errno = 0;
661
662 if ( ss->codecfilter == -1 ) {
663  if ( write(fh, obuf, olen) == olen ) {
664   if ( need_to_free ) free(obuf);
665   return 0;
666  }
667 } else {
668  if ( codecfilter_write(ss->codecfilter_inst, ss->codecfilter, obuf, olen)
669            == olen ) {
670   if ( need_to_free ) free(obuf);
671   return 0;
672  } else { // we cann't retry on codec filetered streams
673   if ( need_to_free ) free(obuf);
674   streams_delete(id);
675   return -1;
676  }
677 }
678
679 if ( errno == EAGAIN ) {
680  // ok, the client blocks for a moment, we try to sleep a bit an retry in the hope not to
681  // make any gapes in any output because of this
682
683  usleep(100); // 0.1ms
684
685  if ( write(fh, obuf, olen) == olen ) {
686   if ( need_to_free ) free(obuf);
687   return 0;
688  }
689 }
690
691 // ug... error... delete stream!
692
693 if ( need_to_free ) free(obuf);
694 streams_delete(id);
695
696 return -1;
697}
698
699int streams_send_filter(int id) {
700 int fh;
701 int have = 0;
702 int len;
703 struct roar_stream        *   s;
704 struct roar_stream_server *  ss;
705
706 if ( g_streams[id] == NULL )
707  return -1;
708
709 ROAR_DBG("streams_send_filter(id=%i) = ?", id);
710
711 s = ROAR_STREAM(ss = g_streams[id]);
712
713 if ( (fh = s->fh) == -1 )
714  return 0;
715
716 if ( s->dir != ROAR_DIR_FILTER )
717  return 0;
718
719 ROAR_DBG("streams_send_filter(id=%i): fh = %i", id, fh);
720
721 if ( write(fh, g_output_buffer, g_output_buffer_len) == g_output_buffer_len ) {
722  while ( have < g_output_buffer_len ) {
723   if ( (len = read(fh, g_output_buffer+have, g_output_buffer_len-have)) < 1 ) {
724    streams_delete(id);
725    return -1;
726   }
727   have += len;
728  }
729  return 0;
730 }
731
732 // ug... error... delete stream!
733
734 streams_delete(id);
735
736 return -1;
737}
738
739
740// VIO:
741
742ssize_t stream_vio_read (int stream, void *buf, size_t count) {
743 struct roar_stream_server * s = g_streams[stream];
744
745 if ( !s )
746  return -1;
747
748 return stream_vio_s_read(s, buf, count);
749}
750
751ssize_t stream_vio_write(int stream, void *buf, size_t count) {
752 struct roar_stream_server * s = g_streams[stream];
753
754 if ( !s )
755  return -1;
756
757 return stream_vio_s_write(s, buf, count);
758}
759
760
761ssize_t stream_vio_s_read (struct roar_stream_server * stream, void *buf, size_t count) {
762 errno = 0;
763
764 if ( !stream )
765  return -1;
766
767 if ( ! stream->vio.read )
768  return -1;
769
770 return stream->vio.read(ROAR_STREAM(stream)->fh, buf, count, stream->vio.inst);
771}
772
773ssize_t stream_vio_s_write(struct roar_stream_server * stream, void *buf, size_t count) {
774 errno = 0;
775
776 if ( !stream )
777  return -1;
778
779 if ( ! stream->vio.write )
780  return -1;
781
782 return stream->vio.write(ROAR_STREAM(stream)->fh, buf, count, stream->vio.inst);
783}
784
785//ll
Note: See TracBrowser for help on using the repository browser.