Skip to content
Snippets Groups Projects
channel.c 123 KiB
Newer Older
		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 */