diff options
author | David M. Lee <dlee@digium.com> | 2013-08-06 14:44:45 +0000 |
---|---|---|
committer | David M. Lee <dlee@digium.com> | 2013-08-06 14:44:45 +0000 |
commit | c79084879427750dc848834a8390bb0c8468f24b (patch) | |
tree | 2e6c4371cda2c73172642b267aa54dea469d600d /main/app.c | |
parent | b97d318b7bc31b47fbd4b74421e351095f05139d (diff) |
ARI: Add recording controls
This patch implements the controls from ARI recordings. The controls
are:
* DELETE /recordings/live/{recordingName} - stop recording and
discard it
* POST /recordings/live/{recordingName}/stop - stop recording
* POST /recordings/live/{recordingName}/pause - pause recording
* POST /recordings/live/{recordingName}/unpause - resume recording
* POST /recordings/live/{recordingName}/mute - mute recording (record
silence to the file)
* POST /recordings/live/{recordingName}/unmute - unmute recording.
Since this underlying functionality did not already exist, is was
added to app.c by a set of control frames, similar to how playback
control works. The pause/mute control frames are toggles, even though
the ARI controls are idempotent, to be consistent with the playback
control frames.
(closes issue ASTERISK-22181)
Review: https://reviewboard.asterisk.org/r/2697/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396331 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/app.c')
-rw-r--r-- | main/app.c | 168 |
1 files changed, 155 insertions, 13 deletions
diff --git a/main/app.c b/main/app.c index 8d081fe8c..ee2bbf467 100644 --- a/main/app.c +++ b/main/app.c @@ -1145,6 +1145,78 @@ int ast_play_and_wait(struct ast_channel *chan, const char *fn) return d; } +/*! + * \brief Construct a silence frame of the same duration as \a orig. + * + * The \a orig frame must be \ref AST_FORMAT_SLINEAR. + * + * \param orig Frame as basis for silence to generate. + * \return New frame of silence; free with ast_frfree(). + * \return \c NULL on error. + */ +static struct ast_frame *make_silence(const struct ast_frame *orig) +{ + struct ast_frame *silence; + size_t size; + size_t datalen; + size_t samples = 0; + struct ast_frame *next; + + if (!orig) { + return NULL; + } + + if (orig->subclass.format.id != AST_FORMAT_SLINEAR) { + ast_log(LOG_WARNING, "Attempting to silence non-slin frame\n"); + return NULL; + } + + for (next = AST_LIST_NEXT(orig, frame_list); + orig; + orig = next, next = orig ? AST_LIST_NEXT(orig, frame_list) : NULL) { + samples += orig->samples; + } + + ast_verb(4, "Silencing %zd samples\n", samples); + + + datalen = sizeof(short) * samples; + size = sizeof(*silence) + datalen; + silence = ast_calloc(1, size); + if (!silence) { + return NULL; + } + + silence->mallocd = AST_MALLOCD_HDR; + silence->frametype = AST_FRAME_VOICE; + silence->data.ptr = (void *)(silence + 1); + silence->samples = samples; + silence->datalen = datalen; + + ast_format_set(&silence->subclass.format, AST_FORMAT_SLINEAR, 0); + + return silence; +} + +/*! + * \brief Sets a channel's read format to \ref AST_FORMAT_SLINEAR, recording + * its original format. + * + * \param chan Channel to modify. + * \param[out] orig_format Output variable to store channel's original read + * format. + * \return 0 on success. + * \return -1 on error. + */ +static int set_read_to_slin(struct ast_channel *chan, struct ast_format *orig_format) +{ + if (!chan || !orig_format) { + return -1; + } + ast_format_copy(orig_format, ast_channel_readformat(chan)); + return ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR); +} + static int global_silence_threshold = 128; static int global_maxsilence = 0; @@ -1274,8 +1346,7 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, return -1; } ast_dsp_set_threshold(sildet, silencethreshold); - ast_format_copy(&rfmt, ast_channel_readformat(chan)); - res = ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR); + res = set_read_to_slin(chan, &rfmt); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); ast_dsp_free(sildet); @@ -1293,9 +1364,15 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, } if (x == fmtcnt) { - /* Loop forever, writing the packets we read to the writer(s), until - we read a digit or get a hangup */ + /* Loop, writing the packets we read to the writer(s), until + * we have reason to stop. */ struct ast_frame *f; + int paused = 0; + int muted = 0; + time_t pause_start = 0; + int paused_secs = 0; + int pausedsilence = 0; + for (;;) { if (!(res = ast_waitfor(chan, 2000))) { ast_debug(1, "One waitfor failed, trying another\n"); @@ -1315,11 +1392,29 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, } if (f->frametype == AST_FRAME_VOICE) { /* write each format */ - for (x = 0; x < fmtcnt; x++) { - if (prepend && !others[x]) { - break; + if (paused) { + /* It's all good */ + res = 0; + } else { + RAII_VAR(struct ast_frame *, silence, NULL, ast_frame_dtor); + struct ast_frame *orig = f; + + if (muted) { + silence = make_silence(orig); + if (!silence) { + ast_log(LOG_WARNING, + "Error creating silence\n"); + break; + } + f = silence; } - res = ast_writestream(others[x], f); + for (x = 0; x < fmtcnt; x++) { + if (prepend && !others[x]) { + break; + } + res = ast_writestream(others[x], f); + } + f = orig; } /* Silence Detection */ @@ -1331,6 +1426,17 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, } olddspsilence = dspsilence; + if (paused) { + /* record how much silence there was while we are paused */ + pausedsilence = dspsilence; + } else if (dspsilence > pausedsilence) { + /* ignore the paused silence */ + dspsilence -= pausedsilence; + } else { + /* dspsilence has reset, reset pausedsilence */ + pausedsilence = 0; + } + if (dspsilence > maxsilence) { /* Ended happily with silence */ ast_verb(3, "Recording automatically stopped after a silence of %d seconds\n", dspsilence/1000); @@ -1362,15 +1468,51 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, break; } if (strchr(canceldtmf, f->subclass.integer)) { - ast_verb(3, "User cancelled message by pressing %c\n", f->subclass.integer); + ast_verb(3, "User canceled message by pressing %c\n", f->subclass.integer); res = f->subclass.integer; outmsg = 0; break; } + } else if (f->frametype == AST_FRAME_CONTROL) { + if (f->subclass.integer == AST_CONTROL_RECORD_CANCEL) { + ast_verb(3, "Message canceled by control\n"); + outmsg = 0; /* cancels the recording */ + res = 0; + break; + } else if (f->subclass.integer == AST_CONTROL_RECORD_STOP) { + ast_verb(3, "Message ended by control\n"); + res = 0; + break; + } else if (f->subclass.integer == AST_CONTROL_RECORD_SUSPEND) { + paused = !paused; + ast_verb(3, "Message %spaused by control\n", + paused ? "" : "un"); + if (paused) { + pause_start = time(NULL); + } else { + paused_secs += time(NULL) - pause_start; + } + } else if (f->subclass.integer == AST_CONTROL_RECORD_MUTE) { + muted = !muted; + ast_verb(3, "Message %smuted by control\n", + muted ? "" : "un"); + /* We can only silence slin frames, so + * set the mode, if we haven't already + * for sildet + */ + if (muted && !rfmt.id) { + ast_verb(3, "Setting read format to linear mode\n"); + res = set_read_to_slin(chan, &rfmt); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); + break; + } + } + } } - if (maxtime) { + if (maxtime && !paused) { end = time(NULL); - if (maxtime < (end - start)) { + if (maxtime < (end - start - paused_secs)) { ast_verb(3, "Took too long, cutting it short...\n"); res = 't'; outmsg = 2; @@ -1493,9 +1635,9 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, static const char default_acceptdtmf[] = "#"; static const char default_canceldtmf[] = ""; -int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists) +int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists) { - return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf), skip_confirmation_sound, if_exists); + return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, beep, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf), skip_confirmation_sound, if_exists); } int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence, const char *path) |