Skip to content
Snippets Groups Projects
audiohook.c 49.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE);
    
    				if (ast_channel_is_bridged(chan)) {
    					ast_channel_set_unbridged_nolock(chan, 1);
    				}
    
    			audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
    
    			if (ast_slinfactory_available(factory) >= samples && ast_slinfactory_read(factory, read_buf, samples)) {
    
    				/* Take audio from this whisper source and combine it into our main buffer */
    
    Olle Johansson's avatar
    Olle Johansson committed
    				for (i = 0, data1 = combine_buf, data2 = read_buf; i < samples; i++, data1++, data2++) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    				}
    
    		/* We take all of the combined whisper sources and combine them into the audio being written out */
    
    		for (i = 0, data1 = middle_frame->data.ptr, data2 = combine_buf; i < samples; i++, data1++, data2++) {
    
    	}
    
    	/* Pass off frame to manipulate audiohooks */
    	if (!AST_LIST_EMPTY(&audiohook_list->manipulate_list)) {
    		AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
    			ast_audiohook_lock(audiohook);
    			if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
    
    				AST_LIST_REMOVE_CURRENT(list);
    
    				ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE);
    
    				ast_audiohook_unlock(audiohook);
    				/* We basically drop all of our links to the manipulate audiohook and prod it to do it's own destructive things */
    				audiohook->manipulate_callback(audiohook, chan, NULL, direction);
    
    				if (ast_channel_is_bridged(chan)) {
    					ast_channel_set_unbridged_nolock(chan, 1);
    				}
    
    			audiohook_list_set_hook_rate(audiohook_list, audiohook, &internal_sample_rate);
    			/*
    			 * Feed in frame to manipulation.
    			 */
    
    			if (!audiohook->manipulate_callback(audiohook, chan, middle_frame, direction)) {
    				/*
    				 * XXX FAILURES ARE IGNORED XXX
    				 * If the manipulation fails then the frame will be returned in its original state.
    				 * Since there are potentially more manipulator callbacks in the list, no action should
    				 * be taken here to exit early.
    				 */
    				middle_frame_manipulated = 1;
    			}
    
    	/* ---Part_3: Decide what to do with the end_frame (whether to transcode or not) */
    
    		if (!(end_frame = audiohook_list_translate_to_native(audiohook_list, direction, middle_frame, start_frame->subclass.format))) {
    
    			/* translation failed, so just pass back the input frame */
    			end_frame = start_frame;
    
    		end_frame = start_frame;
    	}
    	/* clean up our middle_frame if required */
    	if (middle_frame != end_frame) {
    
    		ast_frfree(middle_frame);
    
    		middle_frame = NULL;
    	}
    
    	/* Before returning, if an audiohook got removed, reset samplerate compatibility */
    	if (removed) {
    		audiohook_list_set_samplerate_compatibility(audiohook_list);
    
    	} else {
    		/*
    		 * Set the audiohook_list's rate to the updated rate. Note that if a hook
    		 * was removed then the list's internal rate is reset to the default.
    		 */
    		audiohook_list->list_internal_samp_rate = internal_sample_rate;
    
    int ast_audiohook_write_list_empty(struct ast_audiohook_list *audiohook_list)
    {
    
    Richard Mudgett's avatar
    Richard Mudgett committed
    	return !audiohook_list
    		|| (AST_LIST_EMPTY(&audiohook_list->spy_list)
    			&& AST_LIST_EMPTY(&audiohook_list->whisper_list)
    			&& AST_LIST_EMPTY(&audiohook_list->manipulate_list));
    
    /*! \brief Pass a frame off to be handled by the audiohook core
     * \param chan Channel that the list is coming off of
     * \param audiohook_list List of audiohooks
     * \param direction Direction frame is coming in from
     * \param frame The frame itself
     * \return Return frame on success, NULL on failure
     */
    struct ast_frame *ast_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame)
    {
    	/* Pass off frame to it's respective list write function */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (frame->frametype == AST_FRAME_VOICE) {
    
    		return audio_audiohook_write_list(chan, audiohook_list, direction, frame);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	} else if (frame->frametype == AST_FRAME_DTMF) {
    
    		return dtmf_audiohook_write_list(chan, audiohook_list, direction, frame);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	} else {
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    
    }
    
    /*! \brief Wait for audiohook trigger to be triggered
     * \param audiohook Audiohook to wait on
     */
    void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
    {
    
    	wait = ast_tvadd(ast_tvnow(), ast_samp2tv(50000, 1000));
    	ts.tv_sec = wait.tv_sec;
    	ts.tv_nsec = wait.tv_usec * 1000;
    
    	ast_cond_timedwait(&audiohook->trigger, &audiohook->lock, &ts);
    
    
    /* Count number of channel audiohooks by type, regardless of type */
    int ast_channel_audiohook_count_by_source(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
    {
    	int count = 0;
    	struct ast_audiohook *ah = NULL;
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (!ast_channel_audiohooks(chan)) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    
    
    	switch (type) {
    		case AST_AUDIOHOOK_TYPE_SPY:
    
    			AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->spy_list, ah, list) {
    
    				if (!strcmp(ah->source, source)) {
    					count++;
    				}
    			}
    			break;
    		case AST_AUDIOHOOK_TYPE_WHISPER:
    
    			AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->whisper_list, ah, list) {
    
    				if (!strcmp(ah->source, source)) {
    					count++;
    				}
    			}
    			break;
    		case AST_AUDIOHOOK_TYPE_MANIPULATE:
    
    			AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->manipulate_list, ah, list) {
    
    				if (!strcmp(ah->source, source)) {
    					count++;
    				}
    			}
    			break;
    		default:
    
    			ast_debug(1, "Invalid audiohook type supplied, (%u)\n", type);
    
    			return -1;
    	}
    
    	return count;
    }
    
    /* Count number of channel audiohooks by type that are running */
    int ast_channel_audiohook_count_by_source_running(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
    {
    	int count = 0;
    	struct ast_audiohook *ah = NULL;
    
    	if (!ast_channel_audiohooks(chan))
    
    		return -1;
    
    	switch (type) {
    		case AST_AUDIOHOOK_TYPE_SPY:
    
    			AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->spy_list, ah, list) {
    
    				if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
    					count++;
    			}
    			break;
    		case AST_AUDIOHOOK_TYPE_WHISPER:
    
    			AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->whisper_list, ah, list) {
    
    				if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
    					count++;
    			}
    			break;
    		case AST_AUDIOHOOK_TYPE_MANIPULATE:
    
    			AST_LIST_TRAVERSE(&ast_channel_audiohooks(chan)->manipulate_list, ah, list) {
    
    				if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
    					count++;
    			}
    			break;
    		default:
    
    			ast_debug(1, "Invalid audiohook type supplied, (%u)\n", type);
    
    /*! \brief Audiohook volume adjustment structure */
    struct audiohook_volume {
    	struct ast_audiohook audiohook; /*!< Audiohook attached to the channel */
    	int read_adjustment;            /*!< Value to adjust frames read from the channel by */
    	int write_adjustment;           /*!< Value to adjust frames written to the channel by */
    };
    
    /*! \brief Callback used to destroy the audiohook volume datastore
     * \param data Volume information structure
     * \return Returns nothing
     */
    static void audiohook_volume_destroy(void *data)
    {
    	struct audiohook_volume *audiohook_volume = data;
    
    	/* Destroy the audiohook as it is no longer in use */
    	ast_audiohook_destroy(&audiohook_volume->audiohook);
    
    	/* Finally free ourselves, we are of no more use */
    	ast_free(audiohook_volume);
    
    	return;
    }
    
    /*! \brief Datastore used to store audiohook volume information */
    static const struct ast_datastore_info audiohook_volume_datastore = {
    	.type = "Volume",
    	.destroy = audiohook_volume_destroy,
    };
    
    /*! \brief Helper function which actually gets called by audiohooks to perform the adjustment
     * \param audiohook Audiohook attached to the channel
     * \param chan Channel we are attached to
     * \param frame Frame of audio we want to manipulate
     * \param direction Direction the audio came in from
     * \return Returns 0 on success, -1 on failure
     */
    static int audiohook_volume_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
    {
    	struct ast_datastore *datastore = NULL;
    	struct audiohook_volume *audiohook_volume = NULL;
    	int *gain = NULL;
    
    	/* If the audiohook is shutting down don't even bother */
    	if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE) {
    		return 0;
    	}
    
    	/* Try to find the datastore containg adjustment information, if we can't just bail out */
    	if (!(datastore = ast_channel_datastore_find(chan, &audiohook_volume_datastore, NULL))) {
    		return 0;
    	}
    
    	audiohook_volume = datastore->data;
    
    	/* Based on direction grab the appropriate adjustment value */
    	if (direction == AST_AUDIOHOOK_DIRECTION_READ) {
    		gain = &audiohook_volume->read_adjustment;
    	} else if (direction == AST_AUDIOHOOK_DIRECTION_WRITE) {
    		gain = &audiohook_volume->write_adjustment;
    	}
    
    	/* If an adjustment value is present modify the frame */
    	if (gain && *gain) {
    		ast_frame_adjust_volume(frame, *gain);
    	}
    
    	return 0;
    }
    
    /*! \brief Helper function which finds and optionally creates an audiohook_volume_datastore datastore on a channel
     * \param chan Channel to look on
     * \param create Whether to create the datastore if not found
     * \return Returns audiohook_volume structure on success, NULL on failure
     */
    static struct audiohook_volume *audiohook_volume_get(struct ast_channel *chan, int create)
    {
    	struct ast_datastore *datastore = NULL;
    	struct audiohook_volume *audiohook_volume = NULL;
    
    	/* If we are able to find the datastore return the contents (which is actually an audiohook_volume structure) */
    	if ((datastore = ast_channel_datastore_find(chan, &audiohook_volume_datastore, NULL))) {
    		return datastore->data;
    	}
    
    	/* If we are not allowed to create a datastore or if we fail to create a datastore, bail out now as we have nothing for them */
    
    	if (!create || !(datastore = ast_datastore_alloc(&audiohook_volume_datastore, NULL))) {
    
    		return NULL;
    	}
    
    	/* Create a new audiohook_volume structure to contain our adjustments and audiohook */
    	if (!(audiohook_volume = ast_calloc(1, sizeof(*audiohook_volume)))) {
    
    		return NULL;
    	}
    
    	/* Setup our audiohook structure so we can manipulate the audio */
    
    	ast_audiohook_init(&audiohook_volume->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
    
    	audiohook_volume->audiohook.manipulate_callback = audiohook_volume_callback;
    
    	/* Attach the audiohook_volume blob to the datastore and attach to the channel */
    	datastore->data = audiohook_volume;
    	ast_channel_datastore_add(chan, datastore);
    
    	/* All is well... put the audiohook into motion */
    	ast_audiohook_attach(chan, &audiohook_volume->audiohook);
    
    	return audiohook_volume;
    }
    
    /*! \brief Adjust the volume on frames read from or written to a channel
     * \param chan Channel to muck with
     * \param direction Direction to set on
     * \param volume Value to adjust the volume by
     * \return Returns 0 on success, -1 on failure
     */
    int ast_audiohook_volume_set(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
    {
    	struct audiohook_volume *audiohook_volume = NULL;
    
    	/* Attempt to find the audiohook volume information, but only create it if we are not setting the adjustment value to zero */
    	if (!(audiohook_volume = audiohook_volume_get(chan, (volume ? 1 : 0)))) {
    		return -1;
    	}
    
    	/* Now based on the direction set the proper value */
    	if (direction == AST_AUDIOHOOK_DIRECTION_READ || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
    		audiohook_volume->read_adjustment = volume;
    
    	}
    	if (direction == AST_AUDIOHOOK_DIRECTION_WRITE || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
    
    		audiohook_volume->write_adjustment = volume;
    	}
    
    	return 0;
    }
    
    /*! \brief Retrieve the volume adjustment value on frames read from or written to a channel
     * \param chan Channel to retrieve volume adjustment from
     * \param direction Direction to retrieve
     * \return Returns adjustment value
     */
    int ast_audiohook_volume_get(struct ast_channel *chan, enum ast_audiohook_direction direction)
    {
    	struct audiohook_volume *audiohook_volume = NULL;
    	int adjustment = 0;
    
    	/* Attempt to find the audiohook volume information, but do not create it as we only want to look at the values */
    	if (!(audiohook_volume = audiohook_volume_get(chan, 0))) {
    		return 0;
    	}
    
    	/* Grab the adjustment value based on direction given */
    	if (direction == AST_AUDIOHOOK_DIRECTION_READ) {
    		adjustment = audiohook_volume->read_adjustment;
    	} else if (direction == AST_AUDIOHOOK_DIRECTION_WRITE) {
    		adjustment = audiohook_volume->write_adjustment;
    	}
    
    	return adjustment;
    }
    
    /*! \brief Adjust the volume on frames read from or written to a channel
     * \param chan Channel to muck with
     * \param direction Direction to increase
     * \param volume Value to adjust the adjustment by
     * \return Returns 0 on success, -1 on failure
     */
    int ast_audiohook_volume_adjust(struct ast_channel *chan, enum ast_audiohook_direction direction, int volume)
    {
    	struct audiohook_volume *audiohook_volume = NULL;
    
    	/* Attempt to find the audiohook volume information, and create an audiohook if none exists */
    	if (!(audiohook_volume = audiohook_volume_get(chan, 1))) {
    		return -1;
    	}
    
    	/* Based on the direction change the specific adjustment value */
    	if (direction == AST_AUDIOHOOK_DIRECTION_READ || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
    		audiohook_volume->read_adjustment += volume;
    
    	}
    	if (direction == AST_AUDIOHOOK_DIRECTION_WRITE || direction == AST_AUDIOHOOK_DIRECTION_BOTH) {
    
    		audiohook_volume->write_adjustment += volume;
    	}
    
    	return 0;
    }
    
    
    /*! \brief Mute frames read from or written to a channel
     * \param chan Channel to muck with
     * \param source Type of audiohook
     * \param flag which flag to set / clear
     * \param clear set or clear
     * \return Returns 0 on success, -1 on failure
     */
    int ast_audiohook_set_mute(struct ast_channel *chan, const char *source, enum ast_audiohook_flags flag, int clear)
    {
    	struct ast_audiohook *audiohook = NULL;
    
    	ast_channel_lock(chan);
    
    	/* Ensure the channel has audiohooks on it */
    
    	if (!ast_channel_audiohooks(chan)) {
    
    		ast_channel_unlock(chan);
    		return -1;
    	}
    
    
    	audiohook = find_audiohook_by_source(ast_channel_audiohooks(chan), source);
    
    
    	if (audiohook) {
    		if (clear) {
    			ast_clear_flag(audiohook, flag);
    		} else {
    			ast_set_flag(audiohook, flag);
    		}
    	}
    
    	ast_channel_unlock(chan);
    
    	return (audiohook ? 0 : -1);
    }