1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Anthony Minessale II <anthm@freeswitch.org>
27  *
28  *
29  * mod_snapshot.c -- record a sliding window of audio and take snapshots to disk
30  *
31  */
32 #include <switch.h>
33 
34 /* Prototypes */
35 SWITCH_MODULE_RUNTIME_FUNCTION(mod_snapshot_runtime);
36 SWITCH_MODULE_LOAD_FUNCTION(mod_snapshot_load);
37 
38 /* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
39  * Defines a switch_loadable_module_function_table_t and a static const char[] modname
40  */
41 SWITCH_MODULE_DEFINITION(mod_snapshot, mod_snapshot_load, NULL, NULL);
42 
43 struct cap_cb {
44 	switch_buffer_t *buffer;
45 	switch_mutex_t *mutex;
46 	char *base;
47 };
48 
capture_callback(switch_media_bug_t * bug,void * user_data,switch_abc_type_t type)49 static switch_bool_t capture_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
50 {
51 	switch_core_session_t *session = switch_core_media_bug_get_session(bug);
52 	switch_channel_t *channel = switch_core_session_get_channel(session);
53 	struct cap_cb *cb = (struct cap_cb *) user_data;
54 
55 	switch (type) {
56 	case SWITCH_ABC_TYPE_INIT:
57 		break;
58 	case SWITCH_ABC_TYPE_CLOSE:
59 		{
60 			if (cb->buffer) {
61 				switch_buffer_destroy(&cb->buffer);
62 			}
63 			switch_channel_set_private(channel, "snapshot", NULL);
64 		}
65 
66 		break;
67 	case SWITCH_ABC_TYPE_READ:
68 
69 		if (cb->buffer) {
70 			uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE];
71 			switch_frame_t frame = { 0 };
72 
73 			frame.data = data;
74 			frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE;
75 
76 			if (switch_mutex_trylock(cb->mutex) == SWITCH_STATUS_SUCCESS) {
77 				while (switch_core_media_bug_read(bug, &frame, SWITCH_TRUE) == SWITCH_STATUS_SUCCESS && !switch_test_flag((&frame), SFF_CNG)) {
78 					if (frame.datalen)
79 						switch_buffer_slide_write(cb->buffer, frame.data, frame.datalen);
80 				}
81 				switch_mutex_unlock(cb->mutex);
82 			}
83 
84 		}
85 		break;
86 	case SWITCH_ABC_TYPE_WRITE:
87 	default:
88 		break;
89 	}
90 
91 	return SWITCH_TRUE;
92 }
93 
start_capture(switch_core_session_t * session,unsigned int seconds,switch_media_bug_flag_t flags,const char * base)94 static switch_status_t start_capture(switch_core_session_t *session, unsigned int seconds, switch_media_bug_flag_t flags, const char *base)
95 {
96 	switch_channel_t *channel = switch_core_session_get_channel(session);
97 	switch_media_bug_t *bug;
98 	switch_status_t status;
99 	switch_codec_implementation_t read_impl = { 0 };
100 	struct cap_cb *cb;
101 	switch_size_t bytes;
102 	switch_bind_flag_t bind_flags = 0;
103 
104 	if (switch_channel_get_private(channel, "snapshot")) {
105 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Already Running.\n");
106 		return SWITCH_STATUS_FALSE;
107 	}
108 
109 	if (seconds < 5) {
110 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Must be at least 5 seconds!\n");
111 		return SWITCH_STATUS_FALSE;
112 	}
113 
114 	switch_core_session_get_read_impl(session, &read_impl);
115 
116 	if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) {
117 		return SWITCH_STATUS_FALSE;
118 	}
119 
120 	cb = switch_core_session_alloc(session, sizeof(*cb));
121 	cb->base = switch_core_session_strdup(session, base);
122 
123 	bytes = read_impl.samples_per_second * seconds * 2;
124 
125 	switch_buffer_create_dynamic(&cb->buffer, bytes, bytes, bytes);
126 	switch_mutex_init(&cb->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
127 
128 	if ((status = switch_core_media_bug_add(session, "snapshot", NULL, capture_callback, cb, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
129 		return status;
130 	}
131 
132 	bind_flags = SBF_DIAL_ALEG | SBF_EXEC_ALEG | SBF_EXEC_SAME;
133 	switch_ivr_bind_dtmf_meta_session(session, 7, bind_flags, "snapshot::snap");
134 
135 	switch_channel_set_private(channel, "snapshot", bug);
136 
137 	return SWITCH_STATUS_SUCCESS;
138 }
139 
do_snap(switch_core_session_t * session)140 static switch_status_t do_snap(switch_core_session_t *session)
141 {
142 	switch_channel_t *channel = switch_core_session_get_channel(session);
143 	switch_media_bug_t *bug = switch_channel_get_private(channel, "snapshot");
144 	char *file;
145 	switch_file_handle_t fh = { 0 };
146 	switch_codec_implementation_t read_impl = { 0 };
147 	switch_size_t bytes_read;
148 	int16_t pdata[4096] = { 0 };
149 
150 	if (bug) {
151 		switch_time_exp_t tm;
152 		switch_size_t retsize;
153 		char date[80] = "";
154 		struct cap_cb *cb = (struct cap_cb *) switch_core_media_bug_get_user_data(bug);
155 
156 		if (!cb) {
157 			return SWITCH_STATUS_FALSE;
158 		}
159 
160 		switch_time_exp_lt(&tm, switch_time_make(switch_epoch_time_now(NULL), 0));
161 		switch_strftime(date, &retsize, sizeof(date), "%Y_%m_%d_%H_%M_%S", &tm);
162 
163 		file = switch_core_session_sprintf(session, "%s%s%s_%s.wav", SWITCH_GLOBAL_dirs.sounds_dir, SWITCH_PATH_SEPARATOR, cb->base, date);
164 
165 		switch_core_session_get_read_impl(session, &read_impl);
166 		fh.channels = 0;
167 		fh.native_rate = read_impl.actual_samples_per_second;
168 
169 		if (switch_core_file_open(&fh,
170 								  file,
171 								  0,
172 								  read_impl.actual_samples_per_second, SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
173 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error opening %s\n", file);
174 			switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
175 			switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);
176 			return SWITCH_STATUS_FALSE;
177 		}
178 
179 		switch_mutex_lock(cb->mutex);
180 		while ((bytes_read = switch_buffer_read(cb->buffer, pdata, sizeof(pdata)))) {
181 			switch_size_t samples = bytes_read / 2;
182 
183 			if (switch_core_file_write(&fh, pdata, &samples) != SWITCH_STATUS_SUCCESS) {
184 				break;
185 			}
186 		}
187 		switch_mutex_unlock(cb->mutex);
188 		switch_core_file_close(&fh);
189 		switch_core_set_variable("file", file);
190 		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Wrote %s\n", file);
191 		return SWITCH_STATUS_SUCCESS;
192 	}
193 
194 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s Bug is not attached.\n", switch_channel_get_name(channel));
195 	return SWITCH_STATUS_FALSE;
196 
197 }
198 
199 #define SNAP_SYNTAX "start <sec> <read|write>"
SWITCH_STANDARD_APP(snapshot_app_function)200 SWITCH_STANDARD_APP(snapshot_app_function)
201 {
202 	char *argv[4] = { 0 };
203 	int argc = 0;
204 	char *lbuf = NULL;
205 	switch_media_bug_flag_t flags = SMBF_READ_STREAM | SMBF_WRITE_STREAM | SMBF_READ_PING;
206 
207 	if (!zstr(data) && (lbuf = switch_core_session_strdup(session, data))) {
208 		argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
209 	}
210 
211 	if (argc < 1) {
212 		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Usage: %s\n", SNAP_SYNTAX);
213 		return;
214 	}
215 
216 	if (!strcasecmp(argv[0], "start")) {
217 		char *sec = argv[1];
218 		char *fl = argv[2];
219 		const char *base = argv[3];
220 		int seconds = 5;
221 
222 		if (sec) {
223 			int tmp = atoi(sec);
224 			if (tmp > 5) {
225 				seconds = tmp;
226 			}
227 		}
228 
229 		if (fl) {
230 			flags = SMBF_READ_PING;
231 			if (switch_stristr("read", fl)) {
232 				flags |= SMBF_READ_STREAM;
233 			}
234 			if (switch_stristr("write", fl)) {
235 				flags |= SMBF_WRITE_STREAM;
236 			}
237 		}
238 
239 		if (!base) {
240 			base = "mod_snapshot";
241 		}
242 
243 		start_capture(session, seconds, flags, base);
244 
245 	}
246 
247 	if (!strcasecmp(argv[0], "snap")) {
248 		do_snap(session);
249 		return;
250 	}
251 }
252 
253 
254 #define SNAP_API_SYNTAX "<uuid> snap|start [<sec> read|write <base>]"
SWITCH_STANDARD_API(snapshot_function)255 SWITCH_STANDARD_API(snapshot_function)
256 {
257 	char *mycmd = NULL, *argv[5] = { 0 };
258 	int argc = 0;
259 	switch_status_t status = SWITCH_STATUS_FALSE;
260 
261 	if (!zstr(cmd) && (mycmd = strdup(cmd))) {
262 		argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
263 	}
264 
265 	if (zstr(cmd) || argc < 2 || zstr(argv[0])) {
266 		stream->write_function(stream, "-USAGE: %s\n", SNAP_API_SYNTAX);
267 		goto done;
268 	} else {
269 		switch_core_session_t *lsession = NULL;
270 
271 		if ((lsession = switch_core_session_locate(argv[0]))) {
272 			if (!strcasecmp(argv[1], "snap")) {
273 				status = do_snap(lsession);
274 			} else if (!strcasecmp(argv[1], "start")) {
275 				char *sec = argv[2];
276 				char *fl = argv[3];
277 				const char *base = argv[4];
278 				int seconds = 5;
279 				switch_media_bug_flag_t flags = SMBF_READ_STREAM | SMBF_WRITE_STREAM | SMBF_READ_PING;
280 
281 				if (sec) {
282 					int tmp = atoi(sec);
283 					if (tmp > 5) {
284 						seconds = tmp;
285 					}
286 				}
287 
288 				if (fl) {
289 					flags = SMBF_READ_PING;
290 					if (switch_stristr("read", fl)) {
291 						flags |= SMBF_READ_STREAM;
292 					}
293 					if (switch_stristr("write", fl)) {
294 						flags |= SMBF_WRITE_STREAM;
295 					}
296 				}
297 
298 				if (!base) {
299 					base = "mod_snapshot";
300 				}
301 
302 				status = start_capture(lsession, seconds, flags, base);
303 			}
304 
305 			switch_core_session_rwunlock(lsession);
306 		}
307 	}
308 
309 	if (status == SWITCH_STATUS_SUCCESS) {
310 		stream->write_function(stream, "+OK Success\n");
311 	} else {
312 		stream->write_function(stream, "-ERR Operation Failed\n");
313 	}
314 
315   done:
316 
317 	switch_safe_free(mycmd);
318 	return SWITCH_STATUS_SUCCESS;
319 }
320 
321 /* Macro expands to: switch_status_t mod_snapshot_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
SWITCH_MODULE_LOAD_FUNCTION(mod_snapshot_load)322 SWITCH_MODULE_LOAD_FUNCTION(mod_snapshot_load)
323 {
324 	switch_api_interface_t *api_interface;
325 	switch_application_interface_t *app_interface;
326 
327 	/* connect my internal structure to the blank pointer passed to me */
328 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
329 
330 	SWITCH_ADD_API(api_interface, "uuid_snapshot", "Snapshot API", snapshot_function, SNAP_API_SYNTAX);
331 	SWITCH_ADD_APP(app_interface, "snapshot", "", "", snapshot_app_function, SNAP_SYNTAX, SAF_MEDIA_TAP);
332 	switch_console_set_complete("add uuid_snapshot ::console::list_uuid");
333 
334 	/* indicate that the module should continue to be loaded */
335 	return SWITCH_STATUS_SUCCESS;
336 }
337 
338 /* For Emacs:
339  * Local Variables:
340  * mode:c
341  * indent-tabs-mode:t
342  * tab-width:4
343  * c-basic-offset:4
344  * End:
345  * For VIM:
346  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
347  */
348