You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

295 lines
6.2 KiB

/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2015 - 2017
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <dml/dml.h>
#include <dml/dml_id.h>
#include <dml/dml_crypto.h>
#include "dml_config.h"
#include "dml_stream_client_simple.h"
#include "alaw.h"
#include <eth_ar/ulaw.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <endian.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <codec2/codec2.h>
static int fd_dump = -1;
static int last_hour = -1;
static char *dumpdir = "./";
static char *dumpfile = "dml_stream_dump";
static size_t f_datasize = 0;
static bool stddump = false;
static unsigned char wav_header[] = {
// RIFF header
'R', 'I', 'F', 'F',
0, 0, 0, 0, // 36 + f_datasize
'W', 'A', 'V', 'E',
// subchunk
'f', 'm', 't', ' ',
16, 0, 0, 0, // subchunk size
1, 0, // PCM
1, 0, // 1 channel
0x40, 0x1f, 0, 0, // 8000Hz -> 0x1f40
0x80, 0x3e, 0, 0, // 8000 * 2Bytes = 16000
0x02, 0, // bytes per block (1channel of 16 bit)
0x10, 0, // bits per sample
// data subchunk
'd', 'a', 't', 'a',
0, 0, 0, 0 // f_datasize
};
static int finish_wav(int fd, size_t data)
{
unsigned char sizeb[4];
sizeb[0] = data & 0xff;
sizeb[1] = (data >> 8) & 0xff,
sizeb[2] = (data >> 16) & 0xff,
sizeb[3] = (data >> 24) & 0xff,
lseek(fd, 40, SEEK_SET);
write(fd, sizeb, 4);
data += 36;
sizeb[0] = data & 0xff;
sizeb[1] = (data >> 8) & 0xff,
sizeb[2] = (data >> 16) & 0xff,
sizeb[3] = (data >> 24) & 0xff,
lseek(fd, 4, SEEK_SET);
write(fd, sizeb, 4);
return 0;
}
static int data_cb(void *arg, void *data, size_t datasize)
{
static struct CODEC2 *dec = NULL;
static int mode = -1;
time_t now = time(NULL);
struct tm tm_now;
gmtime_r(&now, &tm_now);
if (!stddump && tm_now.tm_hour != last_hour) {
if (fd_dump >= 0) {
printf("Closing dump file\n");
finish_wav(fd_dump, f_datasize);
close(fd_dump);
}
char *dname;
asprintf(&dname, "%s/%04d", dumpdir, tm_now.tm_year + 1900);
mkdir(dname, 0777);
free(dname);
asprintf(&dname, "%s/%04d/%02d", dumpdir, tm_now.tm_year + 1900, tm_now.tm_mon + 1);
mkdir(dname, 0777);
free(dname);
char *fname;
asprintf(&fname, "%s/%04d/%02d/%s.%04d%02d%02d%02d00.wav",
dumpdir,
tm_now.tm_year + 1900, tm_now.tm_mon + 1,
dumpfile,
tm_now.tm_year + 1900,
tm_now.tm_mon + 1, tm_now.tm_mday,
tm_now.tm_hour);
printf("Open new dump file: %s\n", fname);
fd_dump = open(fname, O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
free(fname);
if (fd_dump < 0) {
printf("Failed to open dump file\n");
return -1;
}
last_hour = tm_now.tm_hour;
write(fd_dump, wav_header, sizeof(wav_header));
}
if (datasize <= 8) {
return 0;
}
size_t codecdata = datasize - 8;
size_t nr;
uint8_t *data8 = data;
int prev_mode = mode;
mode = data8[6];
if (mode != prev_mode) {
fprintf(stderr, "Switched to mode %d\n", mode);
}
switch (mode) {
case 'A':
case 'U':
nr = codecdata;
break;
case 's':
case 'S':
nr = codecdata/2;
break;
default:
if (prev_mode != mode) {
if (dec)
codec2_destroy(dec);
dec = codec2_create(mode);
}
if (dec) {
int bpf = codec2_bits_per_frame(dec);
int spf = codec2_samples_per_frame(dec);
nr = codecdata / ((bpf+7)/8) * spf;
} else {
nr = 0;
}
break;
}
int16_t samples[nr];
switch (mode) {
case 'A':
alaw_decode(samples, data8 + 8, nr);
break;
case 'U':
ulaw_decode(samples, data8 + 8, nr);
break;
case 's': {
int b;
union {
uint8_t d8[2];
uint16_t s;
} d2s;
for (b = 0; b < nr; b++) {
d2s.d8[0] = data8[8+b*2+0];
d2s.d8[1] = data8[8+b*2+1];
samples[b] = le16toh(d2s.s);
}
break;
}
case 'S': {
int b;
union {
uint8_t d8[2];
uint16_t s;
} d2s;
for (b = 0; b < nr; b++) {
d2s.d8[0] = data8[8+b*2+0];
d2s.d8[1] = data8[8+b*2+1];
samples[b] = be16toh(d2s.s);
}
break;
}
default:
if (dec) {
int bpf = codec2_bits_per_frame(dec);
int spf = codec2_samples_per_frame(dec);
int frames = nr / spf;
int16_t *speech = samples;
data8 += 8;
while (frames) {
codec2_decode(dec, speech, data8);
speech += spf;
data8 += (bpf+7)/8;
frames--;
}
}
break;
}
size_t i;
for (i = 0; i < nr; i++) {
uint16_t samplele = htole16(samples[i]);
if (write(fd_dump, &samplele, sizeof(uint16_t)) != sizeof(uint16_t))
return -1;
f_datasize += sizeof(uint16_t);
}
return 0;
}
int main(int argc, char **argv)
{
char *file = "dml_stream_client.conf";
char *ca;
char *server;
char *req_id_str;
uint8_t req_id[DML_ID_SIZE];
struct dml_stream_client_simple *dss;
if (argc > 2) {
if (!strcmp(argv[2], "-")) {
stddump = true;
fd_dump = 1;
} else {
file = argv[2];
}
}
if (argc > 3)
dumpfile = argv[3];
if (argc > 4)
dumpdir = argv[4];
if (argc < 2) {
fprintf(stderr, "No id given\n");
return -1;
}
req_id_str = argv[1];
if (dml_config_load(file)) {
fprintf(stderr, "Failed to load config file %s\n", file);
return -1;
}
ca = dml_config_value("ca", NULL, ".");
server = dml_config_value("server", NULL, "localhost");
if (dml_crypto_init(NULL, ca)) {
fprintf(stderr, "Failed to init crypto\n");
return -1;
}
if (dml_str_id(req_id, req_id_str)) {
dss = dml_stream_client_simple_search_create(server, NULL, req_id_str, NULL, DML_MIME_DV_C2, NULL, data_cb, true);
} else {
dss = dml_stream_client_simple_create(server, req_id, NULL, data_cb, true);
}
if (!dss) {
printf("Could not create stream\n");
return -1;
}
g_main_loop_run(g_main_loop_new(NULL, false));
return 0;
}