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

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
  1. /*
  2. Copyright Jeroen Vreeken (jeroen@vreeken.net), 2015 - 2017
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. #define _GNU_SOURCE
  15. #include <dml/dml.h>
  16. #include <dml/dml_id.h>
  17. #include <dml/dml_crypto.h>
  18. #include "dml_config.h"
  19. #include "dml_stream_client_simple.h"
  20. #include "alaw.h"
  21. #include <eth_ar/ulaw.h>
  22. #include <stdlib.h>
  23. #include <unistd.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <endian.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <fcntl.h>
  30. #include <codec2/codec2.h>
  31. static int fd_dump = -1;
  32. static int last_hour = -1;
  33. static char *dumpdir = "./";
  34. static char *dumpfile = "dml_stream_dump";
  35. static size_t f_datasize = 0;
  36. static bool stddump = false;
  37. static unsigned char wav_header[] = {
  38. // RIFF header
  39. 'R', 'I', 'F', 'F',
  40. 0, 0, 0, 0, // 36 + f_datasize
  41. 'W', 'A', 'V', 'E',
  42. // subchunk
  43. 'f', 'm', 't', ' ',
  44. 16, 0, 0, 0, // subchunk size
  45. 1, 0, // PCM
  46. 1, 0, // 1 channel
  47. 0x40, 0x1f, 0, 0, // 8000Hz -> 0x1f40
  48. 0x80, 0x3e, 0, 0, // 8000 * 2Bytes = 16000
  49. 0x02, 0, // bytes per block (1channel of 16 bit)
  50. 0x10, 0, // bits per sample
  51. // data subchunk
  52. 'd', 'a', 't', 'a',
  53. 0, 0, 0, 0 // f_datasize
  54. };
  55. static int finish_wav(int fd, size_t data)
  56. {
  57. unsigned char sizeb[4];
  58. sizeb[0] = data & 0xff;
  59. sizeb[1] = (data >> 8) & 0xff,
  60. sizeb[2] = (data >> 16) & 0xff,
  61. sizeb[3] = (data >> 24) & 0xff,
  62. lseek(fd, 40, SEEK_SET);
  63. write(fd, sizeb, 4);
  64. data += 36;
  65. sizeb[0] = data & 0xff;
  66. sizeb[1] = (data >> 8) & 0xff,
  67. sizeb[2] = (data >> 16) & 0xff,
  68. sizeb[3] = (data >> 24) & 0xff,
  69. lseek(fd, 4, SEEK_SET);
  70. write(fd, sizeb, 4);
  71. return 0;
  72. }
  73. static int data_cb(void *arg, void *data, size_t datasize)
  74. {
  75. static struct CODEC2 *dec = NULL;
  76. static int mode = -1;
  77. time_t now = time(NULL);
  78. struct tm tm_now;
  79. gmtime_r(&now, &tm_now);
  80. if (!stddump && tm_now.tm_hour != last_hour) {
  81. if (fd_dump >= 0) {
  82. printf("Closing dump file\n");
  83. finish_wav(fd_dump, f_datasize);
  84. close(fd_dump);
  85. }
  86. char *dname;
  87. asprintf(&dname, "%s/%04d", dumpdir, tm_now.tm_year + 1900);
  88. mkdir(dname, 0777);
  89. free(dname);
  90. asprintf(&dname, "%s/%04d/%02d", dumpdir, tm_now.tm_year + 1900, tm_now.tm_mon + 1);
  91. mkdir(dname, 0777);
  92. free(dname);
  93. char *fname;
  94. asprintf(&fname, "%s/%04d/%02d/%s.%04d%02d%02d%02d00.wav",
  95. dumpdir,
  96. tm_now.tm_year + 1900, tm_now.tm_mon + 1,
  97. dumpfile,
  98. tm_now.tm_year + 1900,
  99. tm_now.tm_mon + 1, tm_now.tm_mday,
  100. tm_now.tm_hour);
  101. printf("Open new dump file: %s\n", fname);
  102. fd_dump = open(fname, O_WRONLY | O_CREAT,
  103. S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  104. free(fname);
  105. if (fd_dump < 0) {
  106. printf("Failed to open dump file\n");
  107. return -1;
  108. }
  109. last_hour = tm_now.tm_hour;
  110. write(fd_dump, wav_header, sizeof(wav_header));
  111. }
  112. if (datasize <= 8) {
  113. return 0;
  114. }
  115. size_t codecdata = datasize - 8;
  116. size_t nr;
  117. uint8_t *data8 = data;
  118. int prev_mode = mode;
  119. mode = data8[6];
  120. if (mode != prev_mode) {
  121. fprintf(stderr, "Switched to mode %d\n", mode);
  122. }
  123. switch (mode) {
  124. case 'A':
  125. case 'U':
  126. nr = codecdata;
  127. break;
  128. case 's':
  129. case 'S':
  130. nr = codecdata/2;
  131. break;
  132. default:
  133. if (prev_mode != mode) {
  134. if (dec)
  135. codec2_destroy(dec);
  136. dec = codec2_create(mode);
  137. }
  138. if (dec) {
  139. int bpf = codec2_bits_per_frame(dec);
  140. int spf = codec2_samples_per_frame(dec);
  141. nr = codecdata / ((bpf+7)/8) * spf;
  142. } else {
  143. nr = 0;
  144. }
  145. break;
  146. }
  147. int16_t samples[nr];
  148. switch (mode) {
  149. case 'A':
  150. alaw_decode(samples, data8 + 8, nr);
  151. break;
  152. case 'U':
  153. ulaw_decode(samples, data8 + 8, nr);
  154. break;
  155. case 's': {
  156. int b;
  157. union {
  158. uint8_t d8[2];
  159. uint16_t s;
  160. } d2s;
  161. for (b = 0; b < nr; b++) {
  162. d2s.d8[0] = data8[8+b*2+0];
  163. d2s.d8[1] = data8[8+b*2+1];
  164. samples[b] = le16toh(d2s.s);
  165. }
  166. break;
  167. }
  168. case 'S': {
  169. int b;
  170. union {
  171. uint8_t d8[2];
  172. uint16_t s;
  173. } d2s;
  174. for (b = 0; b < nr; b++) {
  175. d2s.d8[0] = data8[8+b*2+0];
  176. d2s.d8[1] = data8[8+b*2+1];
  177. samples[b] = be16toh(d2s.s);
  178. }
  179. break;
  180. }
  181. default:
  182. if (dec) {
  183. int bpf = codec2_bits_per_frame(dec);
  184. int spf = codec2_samples_per_frame(dec);
  185. int frames = nr / spf;
  186. int16_t *speech = samples;
  187. data8 += 8;
  188. while (frames) {
  189. codec2_decode(dec, speech, data8);
  190. speech += spf;
  191. data8 += (bpf+7)/8;
  192. frames--;
  193. }
  194. }
  195. break;
  196. }
  197. size_t i;
  198. for (i = 0; i < nr; i++) {
  199. uint16_t samplele = htole16(samples[i]);
  200. if (write(fd_dump, &samplele, sizeof(uint16_t)) != sizeof(uint16_t))
  201. return -1;
  202. f_datasize += sizeof(uint16_t);
  203. }
  204. return 0;
  205. }
  206. int main(int argc, char **argv)
  207. {
  208. char *file = "dml_stream_client.conf";
  209. char *ca;
  210. char *server;
  211. char *req_id_str;
  212. uint8_t req_id[DML_ID_SIZE];
  213. struct dml_stream_client_simple *dss;
  214. if (argc > 2) {
  215. if (!strcmp(argv[2], "-")) {
  216. stddump = true;
  217. fd_dump = 1;
  218. } else {
  219. file = argv[2];
  220. }
  221. }
  222. if (argc > 3)
  223. dumpfile = argv[3];
  224. if (argc > 4)
  225. dumpdir = argv[4];
  226. if (argc < 2) {
  227. fprintf(stderr, "No id given\n");
  228. return -1;
  229. }
  230. req_id_str = argv[1];
  231. if (dml_config_load(file)) {
  232. fprintf(stderr, "Failed to load config file %s\n", file);
  233. return -1;
  234. }
  235. ca = dml_config_value("ca", NULL, ".");
  236. server = dml_config_value("server", NULL, "localhost");
  237. if (dml_crypto_init(NULL, ca)) {
  238. fprintf(stderr, "Failed to init crypto\n");
  239. return -1;
  240. }
  241. if (dml_str_id(req_id, req_id_str)) {
  242. dss = dml_stream_client_simple_search_create(server, NULL, req_id_str, NULL, DML_MIME_DV_C2, NULL, data_cb, true);
  243. } else {
  244. dss = dml_stream_client_simple_create(server, req_id, NULL, data_cb, true);
  245. }
  246. if (!dss) {
  247. printf("Could not create stream\n");
  248. return -1;
  249. }
  250. g_main_loop_run(g_main_loop_new(NULL, false));
  251. return 0;
  252. }