mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-10 04:45:42 +01:00
aplay -- add features for audio surveilance
Add signal SIGUSR1 to turn over the output file, --max-file-time to cause the output file to turn over automatically, and --use-strftime to create output files based on the current time. Signed-off-by: John Sauter <John_Sauter@systemeyescomputerstore.com> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
0895fcdce2
commit
3b425f8fb5
1 changed files with 181 additions and 6 deletions
187
aplay/aplay.c
187
aplay/aplay.c
|
@ -111,6 +111,10 @@ static int test_position = 0;
|
||||||
static int test_coef = 8;
|
static int test_coef = 8;
|
||||||
static int test_nowait = 0;
|
static int test_nowait = 0;
|
||||||
static snd_output_t *log;
|
static snd_output_t *log;
|
||||||
|
static long long max_file_size = 0;
|
||||||
|
static int max_file_time = 0;
|
||||||
|
static int use_strftime = 0;
|
||||||
|
volatile static int recycle_capture_file = 0;
|
||||||
|
|
||||||
static int fd = -1;
|
static int fd = -1;
|
||||||
static off64_t pbrec_count = LLONG_MAX, fdcount;
|
static off64_t pbrec_count = LLONG_MAX, fdcount;
|
||||||
|
@ -199,7 +203,10 @@ _("Usage: %s [OPTION]... [FILE]...\n"
|
||||||
" --test-coef=# test coeficient for ring buffer position (default 8)\n"
|
" --test-coef=# test coeficient for ring buffer position (default 8)\n"
|
||||||
" expression for validation is: coef * (buffer_size / 2)\n"
|
" expression for validation is: coef * (buffer_size / 2)\n"
|
||||||
" --test-nowait do not wait for ring buffer - eats whole CPU\n"
|
" --test-nowait do not wait for ring buffer - eats whole CPU\n"
|
||||||
" --process-id-file write the process ID here\n")
|
" --max-file-time=# start another output file when the old file has recorded\n"
|
||||||
|
" for this many seconds\n"
|
||||||
|
" --process-id-file write the process ID here\n"
|
||||||
|
" --use-strftime apply the strftime facility to the output file name\n")
|
||||||
, command);
|
, command);
|
||||||
printf(_("Recognized sample formats are:"));
|
printf(_("Recognized sample formats are:"));
|
||||||
for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
|
for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
|
||||||
|
@ -365,6 +372,13 @@ static void signal_handler(int sig)
|
||||||
prg_exit(EXIT_FAILURE);
|
prg_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* call on SIGUSR1 signal. */
|
||||||
|
static void signal_handler_recycle (int sig)
|
||||||
|
{
|
||||||
|
/* flag the capture loop to start a new output file */
|
||||||
|
recycle_capture_file = 1;
|
||||||
|
}
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
OPT_VERSION = 1,
|
OPT_VERSION = 1,
|
||||||
OPT_PERIOD_SIZE,
|
OPT_PERIOD_SIZE,
|
||||||
|
@ -376,7 +390,9 @@ enum {
|
||||||
OPT_TEST_POSITION,
|
OPT_TEST_POSITION,
|
||||||
OPT_TEST_COEF,
|
OPT_TEST_COEF,
|
||||||
OPT_TEST_NOWAIT,
|
OPT_TEST_NOWAIT,
|
||||||
OPT_PROCESS_ID_FILE
|
OPT_MAX_FILE_TIME,
|
||||||
|
OPT_PROCESS_ID_FILE,
|
||||||
|
OPT_USE_STRFTIME
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
|
@ -417,7 +433,9 @@ int main(int argc, char *argv[])
|
||||||
{"test-position", 0, 0, OPT_TEST_POSITION},
|
{"test-position", 0, 0, OPT_TEST_POSITION},
|
||||||
{"test-coef", 1, 0, OPT_TEST_COEF},
|
{"test-coef", 1, 0, OPT_TEST_COEF},
|
||||||
{"test-nowait", 0, 0, OPT_TEST_NOWAIT},
|
{"test-nowait", 0, 0, OPT_TEST_NOWAIT},
|
||||||
|
{"max-file-time", 1, 0, OPT_MAX_FILE_TIME},
|
||||||
{"process-id-file", 1, 0, OPT_PROCESS_ID_FILE},
|
{"process-id-file", 1, 0, OPT_PROCESS_ID_FILE},
|
||||||
|
{"use-strftime", 0, 0, OPT_USE_STRFTIME},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
char *pcm_name = "default";
|
char *pcm_name = "default";
|
||||||
|
@ -607,9 +625,15 @@ int main(int argc, char *argv[])
|
||||||
case OPT_TEST_NOWAIT:
|
case OPT_TEST_NOWAIT:
|
||||||
test_nowait = 1;
|
test_nowait = 1;
|
||||||
break;
|
break;
|
||||||
|
case OPT_MAX_FILE_TIME:
|
||||||
|
max_file_time = strtol(optarg, NULL, 0);
|
||||||
|
break;
|
||||||
case OPT_PROCESS_ID_FILE:
|
case OPT_PROCESS_ID_FILE:
|
||||||
pidfile_name = optarg;
|
pidfile_name = optarg;
|
||||||
break;
|
break;
|
||||||
|
case OPT_USE_STRFTIME:
|
||||||
|
use_strftime = 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, _("Try `%s --help' for more information.\n"), command);
|
fprintf(stderr, _("Try `%s --help' for more information.\n"), command);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -682,6 +706,7 @@ int main(int argc, char *argv[])
|
||||||
signal(SIGINT, signal_handler);
|
signal(SIGINT, signal_handler);
|
||||||
signal(SIGTERM, signal_handler);
|
signal(SIGTERM, signal_handler);
|
||||||
signal(SIGABRT, signal_handler);
|
signal(SIGABRT, signal_handler);
|
||||||
|
signal(SIGUSR1, signal_handler_recycle);
|
||||||
if (interleaved) {
|
if (interleaved) {
|
||||||
if (optind > argc - 1) {
|
if (optind > argc - 1) {
|
||||||
if (stream == SND_PCM_STREAM_PLAYBACK)
|
if (stream == SND_PCM_STREAM_PLAYBACK)
|
||||||
|
@ -2379,13 +2404,98 @@ static void playback(char *name)
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mystrftime
|
||||||
|
*
|
||||||
|
* Variant of strftime(3) that supports additional format
|
||||||
|
* specifiers in the format string.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
*
|
||||||
|
* s - destination string
|
||||||
|
* max - max number of bytes to write
|
||||||
|
* userformat - format string
|
||||||
|
* tm - time information
|
||||||
|
* filenumber - the number of the file, starting at 1
|
||||||
|
*
|
||||||
|
* Returns: number of bytes written to the string s
|
||||||
|
*/
|
||||||
|
size_t mystrftime(char *s, size_t max, const char *userformat,
|
||||||
|
const struct tm *tm, const int filenumber)
|
||||||
|
{
|
||||||
|
char formatstring[PATH_MAX] = "";
|
||||||
|
char tempstring[PATH_MAX] = "";
|
||||||
|
char *format, *tempstr;
|
||||||
|
const char *pos_userformat;
|
||||||
|
|
||||||
|
format = formatstring;
|
||||||
|
|
||||||
|
/* if mystrftime is called with userformat = NULL we return a zero length string */
|
||||||
|
if (userformat == NULL) {
|
||||||
|
*s = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pos_userformat = userformat; *pos_userformat; ++pos_userformat) {
|
||||||
|
if (*pos_userformat == '%') {
|
||||||
|
tempstr = tempstring;
|
||||||
|
tempstr[0] = '\0';
|
||||||
|
switch (*++pos_userformat) {
|
||||||
|
|
||||||
|
case '\0': // end of string
|
||||||
|
--pos_userformat;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'v': // file number
|
||||||
|
sprintf(tempstr, "%02d", filenumber);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // All other codes will be handled by strftime
|
||||||
|
*format++ = '%';
|
||||||
|
*format++ = *pos_userformat;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If a format specifier was found and used, copy the result. */
|
||||||
|
if (tempstr[0]) {
|
||||||
|
while ((*format = *tempstr++) != '\0')
|
||||||
|
++format;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For any other character than % we simply copy the character */
|
||||||
|
*format++ = *pos_userformat;
|
||||||
|
}
|
||||||
|
|
||||||
|
*format = '\0';
|
||||||
|
format = formatstring;
|
||||||
|
return strftime(s, max, format, tm);
|
||||||
|
}
|
||||||
|
|
||||||
static int new_capture_file(char *name, char *namebuf, size_t namelen,
|
static int new_capture_file(char *name, char *namebuf, size_t namelen,
|
||||||
int filecount)
|
int filecount)
|
||||||
{
|
{
|
||||||
/* get a copy of the original filename */
|
|
||||||
char *s;
|
char *s;
|
||||||
char buf[PATH_MAX+1];
|
char buf[PATH_MAX+1];
|
||||||
|
time_t t;
|
||||||
|
struct tm *tmp;
|
||||||
|
|
||||||
|
if (use_strftime) {
|
||||||
|
t = time(NULL);
|
||||||
|
tmp = localtime(&t);
|
||||||
|
if (tmp == NULL) {
|
||||||
|
perror("localtime");
|
||||||
|
prg_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (mystrftime(namebuf, namelen, name, tmp, filecount+1) == 0) {
|
||||||
|
fprintf(stderr, "mystrftime returned 0");
|
||||||
|
prg_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
return filecount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get a copy of the original filename */
|
||||||
strncpy(buf, name, sizeof(buf));
|
strncpy(buf, name, sizeof(buf));
|
||||||
|
|
||||||
/* separate extension from filename */
|
/* separate extension from filename */
|
||||||
|
@ -2417,6 +2527,58 @@ static int new_capture_file(char *name, char *namebuf, size_t namelen,
|
||||||
return filecount;
|
return filecount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create_path
|
||||||
|
*
|
||||||
|
* This function creates a file path, like mkdir -p.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
*
|
||||||
|
* path - the path to create
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on failure
|
||||||
|
* On failure, a message has been printed to stderr.
|
||||||
|
*/
|
||||||
|
int create_path(const char *path)
|
||||||
|
{
|
||||||
|
char *start;
|
||||||
|
mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
|
||||||
|
|
||||||
|
if (path[0] == '/')
|
||||||
|
start = strchr(path + 1, '/');
|
||||||
|
else
|
||||||
|
start = strchr(path, '/');
|
||||||
|
|
||||||
|
while (start) {
|
||||||
|
char *buffer = strdup(path);
|
||||||
|
buffer[start-path] = 0x00;
|
||||||
|
|
||||||
|
if (mkdir(buffer, mode) == -1 && errno != EEXIST) {
|
||||||
|
fprintf(stderr, "Problem creating directory %s", buffer);
|
||||||
|
perror(" ");
|
||||||
|
free(buffer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
start = strchr(start + 1, '/');
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int safe_open(const char *name)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = open64(name, O_WRONLY | O_CREAT, 0644);
|
||||||
|
if (fd == -1) {
|
||||||
|
if (errno != ENOENT || !use_strftime)
|
||||||
|
return -1;
|
||||||
|
if (create_path(name) == 0)
|
||||||
|
fd = open64(name, O_WRONLY | O_CREAT, 0644);
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
static void capture(char *orig_name)
|
static void capture(char *orig_name)
|
||||||
{
|
{
|
||||||
int tostdout=0; /* boolean which describes output stream */
|
int tostdout=0; /* boolean which describes output stream */
|
||||||
|
@ -2429,6 +2591,10 @@ static void capture(char *orig_name)
|
||||||
count = calc_count();
|
count = calc_count();
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
count = LLONG_MAX;
|
count = LLONG_MAX;
|
||||||
|
/* compute the number of bytes per file */
|
||||||
|
max_file_size = max_file_time *
|
||||||
|
snd_pcm_format_size(hwparams.format,
|
||||||
|
hwparams.rate * hwparams.channels);
|
||||||
/* WAVE-file should be even (I'm not sure), but wasting one byte
|
/* WAVE-file should be even (I'm not sure), but wasting one byte
|
||||||
isn't a problem (this can only be in 8 bit mono) */
|
isn't a problem (this can only be in 8 bit mono) */
|
||||||
if (count < LLONG_MAX)
|
if (count < LLONG_MAX)
|
||||||
|
@ -2455,7 +2621,7 @@ static void capture(char *orig_name)
|
||||||
/* open a file to write */
|
/* open a file to write */
|
||||||
if(!tostdout) {
|
if(!tostdout) {
|
||||||
/* upon the second file we start the numbering scheme */
|
/* upon the second file we start the numbering scheme */
|
||||||
if (filecount) {
|
if (filecount || use_strftime) {
|
||||||
filecount = new_capture_file(orig_name, namebuf,
|
filecount = new_capture_file(orig_name, namebuf,
|
||||||
sizeof(namebuf),
|
sizeof(namebuf),
|
||||||
filecount);
|
filecount);
|
||||||
|
@ -2464,7 +2630,8 @@ static void capture(char *orig_name)
|
||||||
|
|
||||||
/* open a new file */
|
/* open a new file */
|
||||||
remove(name);
|
remove(name);
|
||||||
if ((fd = open64(name, O_WRONLY | O_CREAT, 0644)) == -1) {
|
fd = safe_open(name);
|
||||||
|
if (fd < 0) {
|
||||||
perror(name);
|
perror(name);
|
||||||
prg_exit(EXIT_FAILURE);
|
prg_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
@ -2474,6 +2641,8 @@ static void capture(char *orig_name)
|
||||||
rest = count;
|
rest = count;
|
||||||
if (rest > fmt_rec_table[file_type].max_filesize)
|
if (rest > fmt_rec_table[file_type].max_filesize)
|
||||||
rest = fmt_rec_table[file_type].max_filesize;
|
rest = fmt_rec_table[file_type].max_filesize;
|
||||||
|
if (max_file_size && (rest > max_file_size))
|
||||||
|
rest = max_file_size;
|
||||||
|
|
||||||
/* setup sample header */
|
/* setup sample header */
|
||||||
if (fmt_rec_table[file_type].start)
|
if (fmt_rec_table[file_type].start)
|
||||||
|
@ -2481,7 +2650,7 @@ static void capture(char *orig_name)
|
||||||
|
|
||||||
/* capture */
|
/* capture */
|
||||||
fdcount = 0;
|
fdcount = 0;
|
||||||
while (rest > 0) {
|
while (rest > 0 && recycle_capture_file == 0) {
|
||||||
size_t c = (rest <= (off64_t)chunk_bytes) ?
|
size_t c = (rest <= (off64_t)chunk_bytes) ?
|
||||||
(size_t)rest : chunk_bytes;
|
(size_t)rest : chunk_bytes;
|
||||||
size_t f = c * 8 / bits_per_frame;
|
size_t f = c * 8 / bits_per_frame;
|
||||||
|
@ -2496,6 +2665,12 @@ static void capture(char *orig_name)
|
||||||
fdcount += c;
|
fdcount += c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* re-enable SIGUSR1 signal */
|
||||||
|
if (recycle_capture_file) {
|
||||||
|
recycle_capture_file = 0;
|
||||||
|
signal(SIGUSR1, signal_handler_recycle);
|
||||||
|
}
|
||||||
|
|
||||||
/* finish sample container */
|
/* finish sample container */
|
||||||
if (fmt_rec_table[file_type].end && !tostdout) {
|
if (fmt_rec_table[file_type].end && !tostdout) {
|
||||||
fmt_rec_table[file_type].end(fd);
|
fmt_rec_table[file_type].end(fd);
|
||||||
|
|
Loading…
Reference in a new issue