nettoyage du repertoire debian/
[asterisk-app-conference.git] / frame.c
CommitLineData
c575c500
TN
1
2// $Id: frame.c 751 2006-12-11 22:08:45Z sbalea $
3
4/*
5 * app_conference
6 *
7 * A channel independent conference application for Asterisk
8 *
9 * Copyright (C) 2002, 2003 Junghanns.NET GmbH
10 * Copyright (C) 2003, 2004 HorizonLive.com, Inc.
11 * Copyright (C) 2005, 2006 HorizonWimba, Inc.
12 * Copyright (C) 2007 Wimba, Inc.
13 *
14 * Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
15 *
16 * Video Conferencing support added by
17 * Neil Stratford <neils@vipadia.com>
18 * Copyright (C) 2005, 2005 Vipadia Limited
19 *
20 * VAD driven video conferencing, text message support
21 * and miscellaneous enhancements added by
22 * Mihai Balea <mihai at hates dot ms>
23 *
24 * This program may be modified and distributed under the
25 * terms of the GNU General Public License. You should have received
26 * a copy of the GNU General Public License along with this
27 * program; if not, write to the Free Software Foundation, Inc.
28 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 */
30
31#include "asterisk/autoconfig.h"
32#include "frame.h"
33
34conf_frame* mix_frames( conf_frame* frames_in, int speaker_count, int listener_count )
35{
36 if ( frames_in == NULL )
37 return NULL ;
38
39 conf_frame* frames_out = NULL ;
40
41 if ( speaker_count > 1 )
42 {
43 if ( speaker_count == 2 && listener_count == 0 )
44 {
45 // optimize here also?
46 frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ;
47 }
48 else
49 {
50 // mix spoken frames for sending
51 // ( note: this call also releases us from free'ing spoken_frames )
52 frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ;
53 }
54 }
55 else if ( speaker_count == 1 )
56 {
57 // pass-through frames
58 frames_out = mix_single_speaker( frames_in ) ;
59 //printf("mix single speaker\n");
60 }
61 else
62 {
63 // no frames to send, leave frames_out null
64 }
65
66 return frames_out ;
67}
68
69conf_frame* mix_single_speaker( conf_frame* frames_in )
70{
71#ifdef APP_CONFERENCE_DEBUG
72 // ast_log( AST_CONF_DEBUG, "returning single spoken frame\n" ) ;
73
74 //
75 // check input
76 //
77
78 if ( frames_in == NULL )
79 {
80 ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null frame\n" ) ;
81 return NULL ;
82 }
83
84 if ( frames_in->fr == NULL )
85 {
86 ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null data\n" ) ;
87 return NULL ;
88 }
89
90 if ( frames_in->member == NULL )
91 {
92 ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null member\n" ) ;
93 return NULL ;
94 }
95#endif // APP_CONFERENCE_DEBUG
96
97 //
98 // 'mix' the frame
99 //
100
101 // copy orignal frame to converted array so listeners don't need to re-encode it
102 frames_in->converted[ frames_in->member->read_format_index ] = ast_frdup( frames_in->fr ) ;
103
104 // convert frame to slinear, if we have a path
105 frames_in->fr = convert_frame_to_slinear(
106 frames_in->member->to_slinear,
107 frames_in->fr
108 ) ;
109
110 // set the frame's member to null ( i.e. all listeners )
111 frames_in->member = NULL ;
112
113 return frames_in ;
114}
115
116// {
117 //
118 // a little optimization for testing only:
119 // when two speakers ( of the same type ) and no listeners
120 // are in a conference, we just swamp the frame's member pointers
121 //
122/*
123 if (
124 listeners == 0
125 && speakers == 2
126 && cf_spokenFrames->member->read_format == cf_spokenFrames->next->member->write_format
127 && cf_spokenFrames->member->write_format == cf_spokenFrames->next->member->read_format
128 )
129 {
130 struct ast_conf_member* m = NULL ;
131 m = cf_spokenFrames->member ;
132 cf_spokenFrames->member = cf_spokenFrames->next->member ;
133 cf_spokenFrames->next->member = m ;
134 return cf_spokenFrames ;
135 }
136*/
137// }
138
139void set_conf_frame_delivery( conf_frame* frame, struct timeval time )
140{
141 for ( ; frame != NULL ; frame = frame->next )
142 {
143 if ( frame->fr != NULL )
144 {
145 // copy passed timeval to frame's delivery timeval
146 frame->fr->delivery = time ;
147 }
148 }
149
150 return ;
151}
152
153conf_frame* mix_multiple_speakers(
154 conf_frame* frames_in,
155 int speakers,
156 int listeners
157)
158{
159#ifdef APP_CONFERENCE_DEBUG
160 //
161 // check input
162 //
163
164 // no frames to mix
165 if ( ( frames_in == NULL ) || ( frames_in->fr == NULL ) )
166 {
167 ast_log( AST_CONF_DEBUG, "passed spoken frame list was NULL\n" ) ;
168 return NULL ;
169 }
170
171 // if less than two speakers, then no frames to mix
172 if ( speakers < 2 )
173 {
174 ast_log( AST_CONF_DEBUG, "mix_multiple_speakers() called with less than two speakers\n" ) ;
175 return NULL ;
176 }
177#endif // APP_CONFERENCE_DEBUG
178
179 //
180 // at this point we know that there is more than one frame,
181 // and that the frames need to be converted to pcm to be mixed
182 //
183 // now, if there are only two frames and two members,
184 // we can swap them. ( but we'll get to that later. )
185 //
186
187 //
188 // loop through the spoken frames, making a list of spoken members,
189 // and converting gsm frames to slinear frames so we can mix them.
190 //
191
192 // pointer to the spoken frames list
193 conf_frame* cf_spoken = frames_in ;
194
195 // pointer to the new list of mixed frames
196 conf_frame* cf_sendFrames = NULL ;
197
198 while ( cf_spoken != NULL )
199 {
200 //
201 // while we're looping through the spoken frames, we'll
202 // convert the frame to a format suitable for mixing
203 //
204 // if the frame fails to convert, drop it and treat
205 // the speaking member like a listener by not adding
206 // them to the cf_sendFrames list
207 //
208
209 if ( cf_spoken->member == NULL )
210 {
211 ast_log( LOG_WARNING, "unable to determine frame member\n" ) ;
212 }
213 else
214 {
215 // ast_log( AST_CONF_DEBUG, "converting frame to slinear, channel => %s\n", cf_spoken->member->channel_name ) ;
216 cf_spoken->fr = convert_frame_to_slinear(
217 cf_spoken->member->to_slinear,
218 cf_spoken->fr
219 ) ;
220
221 if ( cf_spoken->fr == NULL )
222 {
223 ast_log( LOG_WARNING, "unable to convert frame to slinear\n" ) ;
224 }
225 else
226 {
227 // create new conf frame with last frame as 'next'
228 cf_sendFrames = create_conf_frame( cf_spoken->member, cf_sendFrames, NULL ) ;
229 }
230 }
231
232 // point to the next spoken frame
233 cf_spoken = cf_spoken->next ;
234 }
235
236 // if necessary, add a frame with a null member pointer.
237 // this frame will hold the audio mixed for all listeners
238 if ( listeners > 0 )
239 {
240 cf_sendFrames = create_conf_frame( NULL, cf_sendFrames, NULL ) ;
241 }
242
243 //
244 // mix the audio
245 //
246
247 // convenience pointer that skips over the friendly offset
248 char* cp_listenerData ;
249
250 // pointer to the send frames list
251 conf_frame* cf_send = NULL ;
252
253 for ( cf_send = cf_sendFrames ; cf_send != NULL ; cf_send = cf_send->next )
254 {
255 // allocate a mix buffer which fill large enough memory to
256 // hold a frame, and reset it's memory so we don't get noise
257 char* cp_listenerBuffer = malloc( AST_CONF_BUFFER_SIZE ) ;
258 memset( cp_listenerBuffer, 0x0, AST_CONF_BUFFER_SIZE ) ;
259
260 // point past the friendly offset right to the data
261 cp_listenerData = cp_listenerBuffer + AST_FRIENDLY_OFFSET ;
262
263 // reset the spoken list pointer
264 cf_spoken = frames_in ;
265
266 // really mix the audio
267 for ( ; cf_spoken != NULL ; cf_spoken = cf_spoken->next )
268 {
269 //
270 // if the members are equal, and they
271 // are not null, do not mix them.
272 //
273 if (
274 ( cf_send->member == cf_spoken->member )
275 && ( cf_send->member != NULL )
276 )
277 {
278 // don't mix this frame
279 }
280 else if ( cf_spoken->fr == NULL )
281 {
282 ast_log( LOG_WARNING, "unable to mix conf_frame with null ast_frame\n" ) ;
283 }
284 else
285 {
286 // mix the new frame in with the existing buffer
287 mix_slinear_frames( cp_listenerData, (char*)( cf_spoken->fr->data ), AST_CONF_BLOCK_SAMPLES);//XXX NAS cf_spoken->fr->samples ) ;
288 }
289 }
290
291 // copy a pointer to the frame data to the conf_frame
292 cf_send->mixed_buffer = cp_listenerData ;
293 }
294
295 //
296 // copy the mixed buffer to a new frame
297 //
298
299 // reset the send list pointer
300 cf_send = cf_sendFrames ;
301
302 while ( cf_send != NULL )
303 {
304 cf_send->fr = create_slinear_frame( cf_send->mixed_buffer ) ;
305 cf_send = cf_send->next ;
306 }
307
308 //
309 // clean up the spoken frames we were passed
310 // ( caller will only be responsible for free'ing returns frames )
311 //
312
313 // reset the spoken list pointer
314 cf_spoken = frames_in ;
315
316 while ( cf_spoken != NULL )
317 {
318 // delete the frame
319 cf_spoken = delete_conf_frame( cf_spoken ) ;
320 }
321
322 // return the list of frames for sending
323 return cf_sendFrames ;
324}
325
326
327struct ast_frame* convert_frame_to_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr )
328{
329 // check for null frame
330 if ( fr == NULL )
331 {
332 ast_log( LOG_ERROR, "unable to translate null frame to slinear\n" ) ;
333 return NULL ;
334 }
335
336 // we don't need to duplicate this frame since
337 // the normal translation would free it anyway, so
338 // we'll just pretend we free'd and malloc'd a new one.
339 if ( fr->subclass == AST_FORMAT_SLINEAR )
340 return fr ;
341
342 // check for null translator ( after we've checked that we need to translate )
343 if ( trans == NULL )
344 {
345 ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ;
346 return fr ;
347 }
348
349 // return the converted frame
350 return convert_frame( trans, fr ) ;
351}
352
353struct ast_frame* convert_frame_from_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr )
354{
355 // check for null translator ( after we've checked that we need to translate )
356 if ( trans == NULL )
357 {
358 //ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ;
359 return fr ;
360 }
361
362 // check for null frame
363 if ( fr == NULL )
364 {
365 ast_log( LOG_ERROR, "unable to translate null slinear frame\n" ) ;
366 return NULL ;
367 }
368
369 // if the frame is not slinear, return an error
370 if ( fr->subclass != AST_FORMAT_SLINEAR )
371 {
372 ast_log( LOG_ERROR, "unable to translate non-slinear frame\n" ) ;
373 return NULL ;
374 }
375
376 // return the converted frame
377 return convert_frame( trans, fr ) ;
378}
379
380struct ast_frame* convert_frame( struct ast_trans_pvt* trans, struct ast_frame* fr )
381{
382 if ( trans == NULL )
383 {
384 ast_log( LOG_WARNING, "unable to convert frame with null translator\n" ) ;
385 return NULL ;
386 }
387
388 if ( fr == NULL )
389 {
390 ast_log( LOG_WARNING, "unable to convert null frame\n" ) ;
391 return NULL ;
392 }
393
394 // convert the frame
395 struct ast_frame* translated_frame = ast_translate( trans, fr, 1 ) ;
396
397 // check for errors
398 if ( translated_frame == NULL )
399 {
400 ast_log( LOG_ERROR, "unable to translate frame\n" ) ;
401 return NULL ;
402 }
403
404 // return the translated frame
405 return translated_frame ;
406}
407
408conf_frame* delete_conf_frame( conf_frame* cf )
409{
410 int c;
411 // check for null frames
412 if ( cf == NULL )
413 {
414 ast_log( AST_CONF_DEBUG, "unable to delete null conf frame\n" ) ;
415 return NULL ;
416 }
417
418 // check for frame marked as static
419 if ( cf->static_frame == 1 )
420 return NULL ;
421
422 if ( cf->fr != NULL )
423 {
424 ast_frfree( cf->fr ) ;
425 cf->fr = NULL ;
426 }
427
428 // make sure converted frames are set to null
429 for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c )
430 {
431 if ( cf->converted[ c ] != NULL )
432 {
433 ast_frfree( cf->converted[ c ] ) ;
434 cf->converted[ c ] = NULL ;
435 }
436 }
437
438 // get a pointer to the next frame
439 // in the list so we can return it
440 conf_frame* nf = cf->next ;
441
442 free( cf ) ;
443 cf = NULL ;
444
445 return nf ;
446}
447
448conf_frame* create_conf_frame( struct ast_conf_member* member, conf_frame* next, const struct ast_frame* fr )
449{
450 // pointer to list of mixed frames
451 conf_frame* cf = malloc( sizeof( struct conf_frame ) ) ;
452
453 if ( cf == NULL )
454 {
455 ast_log( LOG_ERROR, "unable to allocate memory for conf frame\n" ) ;
456 return NULL ;
457 }
458
459 //
460 // init with some defaults
461 //
462
463 // make sure converted frames are set to null
464// for ( int c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c )
465// {
466// cf->converted[ c ] = NULL ;
467// }
468
469 memset( (struct ast_frame*)( cf->converted ), 0x0, ( sizeof( struct ast_frame* ) * AC_SUPPORTED_FORMATS ) ) ;
470
471 cf->member = member ;
472 // cf->priority = 0 ;
473
474 cf->prev = NULL ;
475 cf->next = next ;
476
477 cf->static_frame = 0 ;
478
479 // establish relationship to 'next'
480 if ( next != NULL ) next->prev = cf ;
481
482 // this holds the ast_frame pointer
483 cf->fr = ( fr == NULL ) ? NULL : ast_frdup( ( struct ast_frame* )( fr ) ) ;
484
485 // this holds the temporu mix buffer
486 cf->mixed_buffer = NULL ;
487
488 return cf ;
489}
490
491conf_frame* copy_conf_frame( conf_frame* src )
492{
493 //
494 // check inputs
495 //
496
497 if ( src == NULL )
498 {
499 ast_log( AST_CONF_DEBUG, "unable to copy null conf frame\n" ) ;
500 return NULL ;
501 }
502
503 //
504 // copy the frame
505 //
506
507 struct conf_frame *cfr = NULL ;
508
509 // create a new conf frame
510 cfr = create_conf_frame( src->member, NULL, src->fr ) ;
511
512 if ( cfr == NULL )
513 {
514 ast_log( AST_CONF_DEBUG, "unable to create new conf frame for copy\n" ) ;
515 return NULL ;
516 }
517
518 return cfr ;
519}
520
521//
522// Create a TEXT frame based on a given string
523//
524struct ast_frame* create_text_frame(const char *text, int copy)
525{
526 struct ast_frame *f;
527 char *t;
528
529 f = calloc(1, sizeof(struct ast_frame));
530 if ( f == NULL )
531 {
532 ast_log( LOG_ERROR, "unable to allocate memory for text frame\n" ) ;
533 return NULL ;
534 }
535 if ( copy )
536 {
537 t = calloc(strlen(text) + 1, 1);
538 if ( t == NULL )
539 {
540 ast_log( LOG_ERROR, "unable to allocate memory for text data\n" ) ;
541 free(f);
542 return NULL ;
543 }
544 strncpy(t, text, strlen(text));
545 } else
546 {
547 t = (char *)text;
548 }
549
550 f->frametype = AST_FRAME_TEXT;
551 f->offset = 0;
552 f->mallocd = AST_MALLOCD_HDR;
553 if ( copy ) f->mallocd |= AST_MALLOCD_DATA;
554 f->datalen = strlen(t) + 1;
555 f->data = t;
556 f->src = NULL;
557
558 return f;
559}
560
561//
562// slinear frame functions
563//
564
565struct ast_frame* create_slinear_frame( char* data )
566{
567 struct ast_frame* f ;
568
569 f = calloc( 1, sizeof( struct ast_frame ) ) ;
570 if ( f == NULL )
571 {
572 ast_log( LOG_ERROR, "unable to allocate memory for slinear frame\n" ) ;
573 return NULL ;
574 }
575
576 f->frametype = AST_FRAME_VOICE ;
577 f->subclass = AST_FORMAT_SLINEAR ;
578 f->samples = AST_CONF_BLOCK_SAMPLES ;
579 f->offset = AST_FRIENDLY_OFFSET ;
580 f->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_DATA ;
581
582 f->datalen = AST_CONF_FRAME_DATA_SIZE ;
583 f->data = data ;
584
585 f->src = NULL ;
586
587 return f ;
588}
589
590void mix_slinear_frames( char *dst, const char *src, int samples )
591{
592 if ( dst == NULL ) return ;
593 if ( src == NULL ) return ;
594
595 int i, val ;
596
597 for ( i = 0 ; i < samples ; ++i )
598 {
599 val = ( (short*)dst )[i] + ( (short*)src )[i] ;
600
601 if ( val > 0x7fff )
602 {
603 ( (short*)dst )[i] = 0x7fff - 1 ;
604 continue ;
605 }
606 else if ( val < -0x7fff )
607 {
608 ( (short*)dst )[i] = -0x7fff + 1 ;
609 continue ;
610 }
611 else
612 {
613 ( (short*)dst )[i] = val ;
614 continue ;
615 }
616 }
617
618 return ;
619}
620
621//
622// silent frame functions
623//
624
625conf_frame* get_silent_frame( void )
626{
627 static conf_frame* static_silent_frame = NULL ;
628
629 // we'll let this leak until the application terminates
630 if ( static_silent_frame == NULL )
631 {
632 // ast_log( AST_CONF_DEBUG, "creating cached silent frame\n" ) ;
633 struct ast_frame* fr = get_silent_slinear_frame() ;
634
635 static_silent_frame = create_conf_frame( NULL, NULL, fr ) ;
636
637 if ( static_silent_frame == NULL )
638 {
639 ast_log( LOG_WARNING, "unable to create cached silent frame\n" ) ;
640 return NULL ;
641 }
642
643 // init the 'converted' slinear silent frame
644 static_silent_frame->converted[ AC_SLINEAR_INDEX ] = get_silent_slinear_frame() ;
645
646 // mark frame as static so it's not deleted
647 static_silent_frame->static_frame = 1 ;
648 }
649
650 return static_silent_frame ;
651}
652
653struct ast_frame* get_silent_slinear_frame( void )
654{
655 static struct ast_frame* f = NULL ;
656
657 // we'll let this leak until the application terminates
658 if ( f == NULL )
659 {
660 char* data = malloc( AST_CONF_BUFFER_SIZE ) ;
661 memset( data, 0x0, AST_CONF_BUFFER_SIZE ) ;
662 f = create_slinear_frame( data ) ;
663 }
664
665 return f;
666}
667
668
669
670
671
672
673
674
675
676
677
678
679