// $Id: frame.c,v 1.7 2005/10/27 17:53:35 stevek Exp $ /* * app_conference * * A channel independent conference application for Asterisk * * Copyright (C) 2002, 2003 Junghanns.NET GmbH * Copyright (C) 2003, 2004 HorizonLive.com, Inc. * * Klaus-Peter Junghanns * * This program may be modified and distributed under the * terms of the GNU Public License. * */ #include "frame.h" conf_frame* mix_frames( conf_frame* frames_in, int speaker_count, int listener_count ) { if ( frames_in == NULL ) return NULL ; conf_frame* frames_out = NULL ; if ( speaker_count > 1 ) { if ( speaker_count == 2 && listener_count == 0 ) { // optimize here also? frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ; } else { // mix spoken frames for sending // ( note: this call also releases us from free'ing spoken_frames ) frames_out = mix_multiple_speakers( frames_in, speaker_count, listener_count ) ; } } else if ( speaker_count == 1 ) { // pass-through frames frames_out = mix_single_speaker( frames_in ) ; } else { // no frames to send, leave frames_out null } return frames_out ; } conf_frame* mix_single_speaker( conf_frame* frames_in ) { #ifdef APP_CONFERENCE_DEBUG // ast_log( AST_CONF_DEBUG, "returning single spoken frame\n" ) ; // // check input // if ( frames_in == NULL ) { ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null frame\n" ) ; return NULL ; } if ( frames_in->fr == NULL ) { ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null data\n" ) ; return NULL ; } if ( frames_in->member == NULL ) { ast_log( AST_CONF_DEBUG, "unable to mix single spoken frame with null member\n" ) ; return NULL ; } #endif // APP_CONFERENCE_DEBUG // // 'mix' the frame // // copy orignal frame to converted array so listeners don't need to re-encode it frames_in->converted[ frames_in->member->read_format_index ] = ast_frdup( frames_in->fr ) ; // convert frame to slinear, if we have a path frames_in->fr = convert_frame_to_slinear( frames_in->member->to_slinear, frames_in->fr ) ; // set the frame's member to null ( i.e. all listeners ) frames_in->member = NULL ; return frames_in ; } // { // // a little optimization for testing only: // when two speakers ( of the same type ) and no listeners // are in a conference, we just swamp the frame's member pointers // /* if ( listeners == 0 && speakers == 2 && cf_spokenFrames->member->read_format == cf_spokenFrames->next->member->write_format && cf_spokenFrames->member->write_format == cf_spokenFrames->next->member->read_format ) { struct ast_conf_member* m = NULL ; m = cf_spokenFrames->member ; cf_spokenFrames->member = cf_spokenFrames->next->member ; cf_spokenFrames->next->member = m ; return cf_spokenFrames ; } */ // } void set_conf_frame_delivery( conf_frame* frame, struct timeval time ) { for ( ; frame != NULL ; frame = frame->next ) { if ( frame->fr != NULL ) { // copy passed timeval to frame's delivery timeval frame->fr->delivery = time ; } } return ; } conf_frame* mix_multiple_speakers( conf_frame* frames_in, int speakers, int listeners ) { #ifdef APP_CONFERENCE_DEBUG // // check input // // no frames to mix if ( ( frames_in == NULL ) || ( frames_in->fr == NULL ) ) { ast_log( AST_CONF_DEBUG, "passed spoken frame list was NULL\n" ) ; return NULL ; } // if less than two speakers, then no frames to mix if ( speakers < 2 ) { ast_log( AST_CONF_DEBUG, "mix_multiple_speakers() called with less than two speakers\n" ) ; return NULL ; } #endif // APP_CONFERENCE_DEBUG // // at this point we know that there is more than one frame, // and that the frames need to be converted to pcm to be mixed // // now, if there are only two frames and two members, // we can swap them. ( but we'll get to that later. ) // // // loop through the spoken frames, making a list of spoken members, // and converting gsm frames to slinear frames so we can mix them. // // pointer to the spoken frames list conf_frame* cf_spoken = frames_in ; // pointer to the new list of mixed frames conf_frame* cf_sendFrames = NULL ; while ( cf_spoken != NULL ) { // // while we're looping through the spoken frames, we'll // convert the frame to a format suitable for mixing // // if the frame fails to convert, drop it and treat // the speaking member like a listener by not adding // them to the cf_sendFrames list // if ( cf_spoken->member == NULL ) { ast_log( LOG_WARNING, "unable to determine frame member\n" ) ; } else { // ast_log( AST_CONF_DEBUG, "converting frame to slinear, channel => %s\n", cf_spoken->member->channel_name ) ; cf_spoken->fr = convert_frame_to_slinear( cf_spoken->member->to_slinear, cf_spoken->fr ) ; if ( cf_spoken->fr == NULL ) { ast_log( LOG_WARNING, "unable to convert frame to slinear\n" ) ; } else { // create new conf frame with last frame as 'next' cf_sendFrames = create_conf_frame( cf_spoken->member, cf_sendFrames, NULL ) ; } } // point to the next spoken frame cf_spoken = cf_spoken->next ; } // if necessary, add a frame with a null member pointer. // this frame will hold the audio mixed for all listeners if ( listeners > 0 ) { cf_sendFrames = create_conf_frame( NULL, cf_sendFrames, NULL ) ; } // // mix the audio // // convenience pointer that skips over the friendly offset char* cp_listenerData ; // pointer to the send frames list conf_frame* cf_send = NULL ; for ( cf_send = cf_sendFrames ; cf_send != NULL ; cf_send = cf_send->next ) { // allocate a mix buffer which fill large enough memory to // hold a frame, and reset it's memory so we don't get noise char* cp_listenerBuffer = malloc( AST_CONF_BUFFER_SIZE ) ; memset( cp_listenerBuffer, 0x0, AST_CONF_BUFFER_SIZE ) ; // point past the friendly offset right to the data cp_listenerData = cp_listenerBuffer + AST_FRIENDLY_OFFSET ; // reset the spoken list pointer cf_spoken = frames_in ; // really mix the audio for ( ; cf_spoken != NULL ; cf_spoken = cf_spoken->next ) { // // if the members are equal, and they // are not null, do not mix them. // if ( ( cf_send->member == cf_spoken->member ) && ( cf_send->member != NULL ) ) { // don't mix this frame } else if ( cf_spoken->fr == NULL ) { ast_log( LOG_WARNING, "unable to mix conf_frame with null ast_frame\n" ) ; } else { // mix the new frame in with the existing buffer mix_slinear_frames( cp_listenerData, (char*)( cf_spoken->fr->data ), cf_spoken->fr->samples ) ; } } // copy a pointer to the frame data to the conf_frame cf_send->mixed_buffer = cp_listenerData ; } // // copy the mixed buffer to a new frame // // reset the send list pointer cf_send = cf_sendFrames ; while ( cf_send != NULL ) { cf_send->fr = create_slinear_frame( cf_send->mixed_buffer ) ; cf_send = cf_send->next ; } // // clean up the spoken frames we were passed // ( caller will only be responsible for free'ing returns frames ) // // reset the spoken list pointer cf_spoken = frames_in ; while ( cf_spoken != NULL ) { // delete the frame cf_spoken = delete_conf_frame( cf_spoken ) ; } // return the list of frames for sending return cf_sendFrames ; } struct ast_frame* convert_frame_to_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) { // check for null frame if ( fr == NULL ) { ast_log( LOG_ERROR, "unable to translate null frame to slinear\n" ) ; return NULL ; } // we don't need to duplicate this frame since // the normal translation would free it anyway, so // we'll just pretend we free'd and malloc'd a new one. if ( fr->subclass == AST_FORMAT_SLINEAR ) return fr ; // check for null translator ( after we've checked that we need to translate ) if ( trans == NULL ) { // ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ; return fr ; } // return the converted frame return convert_frame( trans, fr ) ; } struct ast_frame* convert_frame_from_slinear( struct ast_trans_pvt* trans, struct ast_frame* fr ) { // check for null translator ( after we've checked that we need to translate ) if ( trans == NULL ) { // ast_log( LOG_ERROR, "unable to translate frame with null translation path\n" ) ; return fr ; } // check for null frame if ( fr == NULL ) { ast_log( LOG_ERROR, "unable to translate null slinear frame\n" ) ; return NULL ; } // if the frame is not slinear, return an error if ( fr->subclass != AST_FORMAT_SLINEAR ) { ast_log( LOG_ERROR, "unable to translate non-slinear frame\n" ) ; return NULL ; } // return the converted frame return convert_frame( trans, fr ) ; } struct ast_frame* convert_frame( struct ast_trans_pvt* trans, struct ast_frame* fr ) { if ( trans == NULL ) { ast_log( LOG_WARNING, "unable to convert frame with null translator\n" ) ; return NULL ; } if ( fr == NULL ) { ast_log( LOG_WARNING, "unable to convert null frame\n" ) ; return NULL ; } // convert the frame struct ast_frame* translated_frame = ast_translate( trans, fr, 1 ) ; // check for errors if ( translated_frame == NULL ) { ast_log( LOG_ERROR, "unable to translate frame\n" ) ; return NULL ; } // return the translated frame return translated_frame ; } conf_frame* delete_conf_frame( conf_frame* cf ) { // check for null frames if ( cf == NULL ) { ast_log( AST_CONF_DEBUG, "unable to delete null conf frame\n" ) ; return NULL ; } // check for frame marked as static if ( cf->static_frame == 1 ) return NULL ; if ( cf->fr != NULL ) { ast_frfree( cf->fr ) ; cf->fr = NULL ; } // make sure converted frames are set to null int c; for ( c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c ) { if ( cf->converted[ c ] != NULL ) { ast_frfree( cf->converted[ c ] ) ; cf->converted[ c ] = NULL ; } } // get a pointer to the next frame // in the list so we can return it conf_frame* nf = cf->next ; free( cf ) ; cf = NULL ; return nf ; } conf_frame* create_conf_frame( struct ast_conf_member* member, conf_frame* next, const struct ast_frame* fr ) { // pointer to list of mixed frames conf_frame* cf = malloc( sizeof( struct conf_frame ) ) ; if ( cf == NULL ) { ast_log( LOG_ERROR, "unable to allocate memory for conf frame\n" ) ; return NULL ; } // // init with some defaults // // make sure converted frames are set to null // for ( int c = 0 ; c < AC_SUPPORTED_FORMATS ; ++c ) // { // cf->converted[ c ] = NULL ; // } memset( (struct ast_frame*)( cf->converted ), 0x0, ( sizeof( struct ast_frame* ) * AC_SUPPORTED_FORMATS ) ) ; cf->member = member ; // cf->priority = 0 ; cf->prev = NULL ; cf->next = next ; cf->static_frame = 0 ; // establish relationship to 'next' if ( next != NULL ) next->prev = cf ; // this holds the ast_frame pointer cf->fr = ( fr == NULL ) ? NULL : ast_frdup( ( struct ast_frame* )( fr ) ) ; // this holds the temporu mix buffer cf->mixed_buffer = NULL ; return cf ; } conf_frame* copy_conf_frame( conf_frame* src ) { // // check inputs // if ( src == NULL ) { ast_log( AST_CONF_DEBUG, "unable to copy null conf frame\n" ) ; return NULL ; } // // copy the frame // struct conf_frame *cfr = NULL ; // create a new conf frame cfr = create_conf_frame( src->member, NULL, src->fr ) ; if ( cfr == NULL ) { ast_log( AST_CONF_DEBUG, "unable to create new conf frame for copy\n" ) ; return NULL ; } return cfr ; } // // slinear frame functions // struct ast_frame* create_slinear_frame( char* data ) { struct ast_frame* f ; f = calloc( 1, sizeof( struct ast_frame ) ) ; if ( f == NULL ) { ast_log( LOG_ERROR, "unable to allocate memory for slinear frame\n" ) ; return NULL ; } f->frametype = AST_FRAME_VOICE ; f->subclass = AST_FORMAT_SLINEAR ; f->samples = AST_CONF_BLOCK_SAMPLES ; f->offset = AST_FRIENDLY_OFFSET ; f->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_DATA ; f->datalen = AST_CONF_FRAME_DATA_SIZE ; f->data = data ; f->src = NULL ; return f ; } void mix_slinear_frames( char *dst, const char *src, int samples ) { if ( dst == NULL ) return ; if ( src == NULL ) return ; int i, val ; for ( i = 0 ; i < samples ; ++i ) { val = ( (short*)dst )[i] + ( (short*)src )[i] ; if ( val > 0x7fff ) { ( (short*)dst )[i] = 0x7fff - 1 ; continue ; } else if ( val < -0x7fff ) { ( (short*)dst )[i] = -0x7fff + 1 ; continue ; } else { ( (short*)dst )[i] = val ; continue ; } } return ; } // // silent frame functions // conf_frame* get_silent_frame( void ) { static conf_frame* static_silent_frame = NULL ; // we'll let this leak until the application terminates if ( static_silent_frame == NULL ) { // ast_log( AST_CONF_DEBUG, "creating cached silent frame\n" ) ; struct ast_frame* fr = get_silent_slinear_frame() ; static_silent_frame = create_conf_frame( NULL, NULL, fr ) ; if ( static_silent_frame == NULL ) { ast_log( LOG_WARNING, "unable to create cached silent frame\n" ) ; return NULL ; } // init the 'converted' slinear silent frame static_silent_frame->converted[ AC_SLINEAR_INDEX ] = get_silent_slinear_frame() ; // mark frame as static so it's not deleted static_silent_frame->static_frame = 1 ; } return static_silent_frame ; } struct ast_frame* get_silent_slinear_frame( void ) { static struct ast_frame* f = NULL ; // we'll let this leak until the application terminates if ( f == NULL ) { char* data = malloc( AST_CONF_BUFFER_SIZE ) ; memset( data, 0x0, AST_CONF_BUFFER_SIZE ) ; f = create_slinear_frame( data ) ; } return ast_frdup( f ) ; }