Skip to content
Snippets Groups Projects
channel.c 124 KiB
Newer Older
  • Learn to ignore specific revisions
  • 					       .subclass = spy->write_queue.format,
    					       .data = write_buf,
    					       .samples = samples,
    					       .datalen = ast_codec_get_len(spy->write_queue.format, samples),
    	};
    
    	/* if a flush has been requested, dump everything in whichever queue is larger */
    	if (ast_test_flag(spy, CHANSPY_TRIGGER_FLUSH)) {
    		if (spy->read_queue.samples > spy->write_queue.samples) {
    			if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST)) {
    				for (result = spy->read_queue.head; result; result = result->next)
    					ast_frame_adjust_volume(result, spy->read_vol_adjustment);
    			}
    			result = spy->read_queue.head;
    			spy->read_queue.head = NULL;
    			spy->read_queue.samples = 0;
    		} else {
    			if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST)) {
    				for (result = spy->write_queue.head; result; result = result->next)
    					ast_frame_adjust_volume(result, spy->write_vol_adjustment);
    			}
    			result = spy->write_queue.head;
    			spy->write_queue.head = NULL;
    			spy->write_queue.samples = 0;
    		}
    
    		ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
    		return result;
    
    	}
    
    	if ((spy->read_queue.samples < samples) || (spy->write_queue.samples < samples))
    		return NULL;
    
    	/* short-circuit if both head frames have exactly what we want */
    	if ((spy->read_queue.head->samples == samples) &&
    	    (spy->write_queue.head->samples == samples)) {
    		read_frame = spy->read_queue.head;
    		spy->read_queue.head = read_frame->next;
    		read_frame->next = NULL;
    
    		write_frame = spy->write_queue.head;
    		spy->write_queue.head = write_frame->next;
    		write_frame->next = NULL;
    
    		spy->read_queue.samples -= samples;
    		spy->write_queue.samples -= samples;
    
    		need_dup = 0;
    	} else {
    		copy_data_from_queue(&spy->read_queue, read_buf, samples);
    		copy_data_from_queue(&spy->write_queue, write_buf, samples);
    
    		read_frame = &stack_read_frame;
    		write_frame = &stack_write_frame;
    		need_dup = 1;
    	}
    	
    	if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST))
    		ast_frame_adjust_volume(read_frame, spy->read_vol_adjustment);
    
    	if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST))
    		ast_frame_adjust_volume(write_frame, spy->write_vol_adjustment);
    
    	if (ast_test_flag(spy, CHANSPY_MIXAUDIO)) {
    		ast_frame_slinear_sum(read_frame, write_frame);
    
    		if (need_dup)
    			result = ast_frdup(read_frame);
    
    	} else {
    		if (need_dup) {
    			result = ast_frdup(read_frame);
    			result->next = ast_frdup(write_frame);
    		} else {
    			result = read_frame;
    			result->next = write_frame;
    		}
    	}
    
    	return result;
    }
    
    
    static void *silence_generator_alloc(struct ast_channel *chan, void *data)
    {
    	/* just store the data pointer in the channel structure */
    	return data;
    }
    
    static void silence_generator_release(struct ast_channel *chan, void *data)
    {
    	/* nothing to do */
    }
    
    
    static int silence_generator_generate(struct ast_channel *chan, void *data, int len, int samples)
    
    	short buf[samples];
    	struct ast_frame frame = {
    		.frametype = AST_FRAME_VOICE,
    		.subclass = AST_FORMAT_SLINEAR,
    		.data = buf,
    		.samples = samples,
    		.datalen = sizeof(buf),
    	};
    
    	memset(buf, 0, sizeof(buf));
    
    	if (ast_write(chan, &frame))
    		return -1;
    
    	return 0;
    }
    
    static struct ast_generator silence_generator = {
    	.alloc = silence_generator_alloc,
    	.release = silence_generator_release,
    
    	.generate = silence_generator_generate,
    
    };
    
    struct ast_silence_generator {
    	int old_write_format;
    };
    
    struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_channel *chan)
    {
    	struct ast_silence_generator *state;
    
    
    	if (!(state = ast_calloc(1, sizeof(*state)))) {
    
    		return NULL;
    	}
    
    	state->old_write_format = chan->writeformat;
    
    	if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
    		ast_log(LOG_ERROR, "Could not set write format to SLINEAR\n");
    		free(state);
    		return NULL;
    	}
    
    	ast_activate_generator(chan, &silence_generator, state);
    
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Started silence generator on '%s'\n", chan->name);
    
    	return state;
    }
    
    void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_silence_generator *state)
    {
    	if (!state)
    		return;
    
    	ast_deactivate_generator(chan);
    
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Stopped silence generator on '%s'\n", chan->name);
    
    	if (ast_set_write_format(chan, state->old_write_format) < 0)
    		ast_log(LOG_ERROR, "Could not return write format to its original state\n");
    
    	free(state);
    }
    
    
    
    /*! \ brief Convert channel reloadreason (ENUM) to text string for manager event */
    
    const char *channelreloadreason2txt(enum channelreloadreason reason)
    {
    
    	case CHANNEL_MODULE_LOAD:
    		return "LOAD (Channel module load)";
    
    	case CHANNEL_MODULE_RELOAD:
    		return "RELOAD (Channel module reload)";
    
    	case CHANNEL_CLI_RELOAD:
    		return "CLIRELOAD (Channel module reload by CLI command)";
    
    	default:
    		return "MANAGERRELOAD (Channel module reload by manager)";
    
    
    #ifdef DEBUG_CHANNEL_LOCKS
    
    /*! \brief Unlock AST channel (and print debugging output) 
    \note You need to enable DEBUG_CHANNEL_LOCKS for this function
    */
    int ast_channel_unlock(struct ast_channel *chan)
    {
    	int res = 0;
    	if (option_debug > 2) 
    		ast_log(LOG_DEBUG, "::::==== Unlocking AST channel %s\n", chan->name);
    	
    	if (!chan) {
    		ast_log(LOG_DEBUG, "::::==== Unlocking non-existing channel \n");
    		return 0;
    	}
    
    	res = ast_mutex_unlock(&chan->lock);
    
    	if (option_debug > 2) {
    		/* Try to find counter if possible on your platform 
    			I've only found out how to do this on Linux
    			DEBUG_THREADS changes the lock structure
    		*/
    #ifdef __linux__
    		int count = 0;
    #ifdef DEBUG_THREADS
    		if ((count = chan->lock.mutex.__m_count))
    #else
    		if ((count = chan->lock.__m_count))
    #endif
    			ast_log(LOG_DEBUG, ":::=== Still have %d locks (recursive)\n", count);
    #endif
    		if (!res)
    			ast_log(LOG_DEBUG, "::::==== Channel %s was unlocked\n", chan->name);
    			if (res == EINVAL) {
    				ast_log(LOG_DEBUG, "::::==== Channel %s had no lock by this thread. Failed unlocking\n", chan->name);
    			}
    		}
    		if (res == EPERM) {
    			/* We had no lock, so okay any way*/
    			if (option_debug > 3)
    				ast_log(LOG_DEBUG, "::::==== Channel %s was not locked at all \n", chan->name);
    		res = 0;
    	}
    	return res;
    }
    
    /*! \brief Lock AST channel (and print debugging output)
    \note You need to enable DEBUG_CHANNEL_LOCKS for this function */
    int ast_channel_lock(struct ast_channel *chan)
    {
    	int res;
    
    	if (option_debug > 3)
    		ast_log(LOG_DEBUG, "====:::: Locking AST channel %s\n", chan->name);
    
    	res = ast_mutex_lock(&chan->lock);
    
    	if (option_debug > 3) {
    #ifdef __linux__
    		int count = 0;
    #ifdef DEBUG_THREADS
    		if ((count = chan->lock.mutex.__m_count))
    #else
    		if ((count = chan->lock.__m_count))
    #endif
    			ast_log(LOG_DEBUG, ":::=== Now have %d locks (recursive)\n", count);
    #endif
    		if (!res)
    			ast_log(LOG_DEBUG, "::::==== Channel %s was locked\n", chan->name);
    		if (res == EDEADLK) {
    		/* We had no lock, so okey any way */
    		if (option_debug > 3)
    			ast_log(LOG_DEBUG, "::::==== Channel %s was not locked by us. Lock would cause deadlock.\n", chan->name);
    		}
    		if (res == EINVAL) {
    			if (option_debug > 3)
    				ast_log(LOG_DEBUG, "::::==== Channel %s lock failed. No mutex.\n", chan->name);
    		}
    	}
    	return res;
    }
    
    /*! \brief Lock AST channel (and print debugging output)
    \note	You need to enable DEBUG_CHANNEL_LOCKS for this function */
    
    int ast_channel_trylock(struct ast_channel *chan)
    
    {
    	int res;
    
    	if (option_debug > 2)
    		ast_log(LOG_DEBUG, "====:::: Trying to lock AST channel %s\n", chan->name);
    
    	res = ast_mutex_trylock(&chan->lock);
    
    	if (option_debug > 2) {
    #ifdef __linux__
    		int count = 0;
    #ifdef DEBUG_THREADS
    		if ((count = chan->lock.mutex.__m_count))
    #else
    		if ((count = chan->lock.__m_count))
    #endif
    			ast_log(LOG_DEBUG, ":::=== Now have %d locks (recursive)\n", count);
    #endif
    		if (!res)
    			ast_log(LOG_DEBUG, "::::==== Channel %s was locked\n", chan->name);
    		if (res == EBUSY) {
    			/* We failed to lock */
    			if (option_debug > 2)
    				ast_log(LOG_DEBUG, "::::==== Channel %s failed to lock. Not waiting around...\n", chan->name);
    		}
    		if (res == EDEADLK) {
    			/* We had no lock, so okey any way*/
    			if (option_debug > 2)
    				ast_log(LOG_DEBUG, "::::==== Channel %s was not locked. Lock would cause deadlock.\n", chan->name);
    		}
    		if (res == EINVAL && option_debug > 2)
    			ast_log(LOG_DEBUG, "::::==== Channel %s lock failed. No mutex.\n", chan->name);
    	}
    	return res;
    }
    
    #endif
    
    
    /*
     * Wrappers for various ast_say_*() functions that call the full version
     * of the same functions.
     * The proper place would be say.c, but that file is optional and one
     * must be able to build asterisk even without it (using a loadable 'say'
     * implementation that only supplies the 'full' version of the functions.
     */
    
    int ast_say_number(struct ast_channel *chan, int num,
    	const char *ints, const char *language, const char *options)
    {
            return ast_say_number_full(chan, num, ints, language, options, -1, -1);
    }
    
    int ast_say_enumeration(struct ast_channel *chan, int num,
    	const char *ints, const char *language, const char *options)
    {
            return ast_say_enumeration_full(chan, num, ints, language, options, -1, -1);
    }
    
    int ast_say_digits(struct ast_channel *chan, int num,
    	const char *ints, const char *lang)
    {
            return ast_say_digits_full(chan, num, ints, lang, -1, -1);
    }
    
    int ast_say_digit_str(struct ast_channel *chan, const char *str,
    	const char *ints, const char *lang)
    {
            return ast_say_digit_str_full(chan, str, ints, lang, -1, -1);
    }
    
    int ast_say_character_str(struct ast_channel *chan, const char *str,
    	const char *ints, const char *lang)
    {
            return ast_say_character_str_full(chan, str, ints, lang, -1, -1);
    }
    
    int ast_say_phonetic_str(struct ast_channel *chan, const char *str,
    	const char *ints, const char *lang)
    {
            return ast_say_phonetic_str_full(chan, str, ints, lang, -1, -1);
    }
    
    int ast_say_digits_full(struct ast_channel *chan, int num,
    	const char *ints, const char *lang, int audiofd, int ctrlfd)
    {
            char buf[256];
    
            snprintf(buf, sizeof(buf), "%d", num);
            return ast_say_digit_str_full(chan, buf, ints, lang, audiofd, ctrlfd);
    }
    
    /* end of file */