make converter_arg in resamplefilter an enum option
[paraslash.git] / afh_recv.c
1 /*
2  * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file afh_recv.c Receiver for streaming local files. */
8
9 #include <regex.h>
10 #include <sys/types.h>
11 #include <lopsub.h>
12
13 #include "recv_cmd.lsg.h"
14 #include "para.h"
15 #include "error.h"
16 #include "list.h"
17 #include "sched.h"
18 #include "buffer_tree.h"
19 #include "recv.h"
20 #include "string.h"
21 #include "fd.h"
22 #include "afh.h"
23
24 struct private_afh_recv_data {
25         int fd;
26         void *map;
27         size_t map_size;
28         struct afh_info afhi;
29         int audio_format_num;
30         long unsigned first_chunk;
31         long unsigned last_chunk;
32         struct timeval stream_start;
33         uint32_t current_chunk;
34 };
35
36 static int afh_execute(struct btr_node *btrn, const char *cmd, char **result)
37 {
38         struct receiver_node *rn = btr_context(btrn);
39         struct private_afh_recv_data *pard = rn->private_data;
40
41         *result = NULL;
42         if (!strcmp(cmd, "seconds_total")) {
43                 *result = make_message("%" PRIu32, pard->afhi.seconds_total);
44                 return 1;
45         }
46         if (!strcmp(cmd, "chunks_total")) {
47                 *result = make_message("%" PRIu32, pard->afhi.chunks_total);
48                 return 1;
49         }
50         if (!strcmp(cmd, "afhi")) {
51                 afh_get_afhi_txt(pard->audio_format_num, &pard->afhi, result);
52                 return 1;
53         }
54         if (!strncmp(cmd, "repos", 5)) {
55                 int32_t x;
56                 int ret = para_atoi32(cmd + 5, &x);
57                 if (ret < 0)
58                         return ret;
59                 if (x >= pard->afhi.chunks_total)
60                         return -ERRNO_TO_PARA_ERROR(EINVAL);
61                 pard->first_chunk = afh_get_start_chunk(x, &pard->afhi);
62                 pard->current_chunk = pard->first_chunk;
63                 return 1;
64         }
65         return -E_BTR_NAVAIL;
66 }
67
68 static int afh_recv_open(struct receiver_node *rn)
69 {
70         struct lls_parse_result *lpr = rn->lpr;
71         struct private_afh_recv_data *pard;
72         struct afh_info *afhi;
73         const char *fn = RECV_CMD_OPT_STRING_VAL(AFH, FILENAME, lpr);
74         int32_t bc = RECV_CMD_OPT_INT32_VAL(AFH, BEGIN_CHUNK, lpr);
75         const struct lls_opt_result *r_e = RECV_CMD_OPT_RESULT(AFH, END_CHUNK, lpr);
76         int ret;
77
78         if (!fn || *fn == '\0')
79                 return -E_AFH_RECV_BAD_FILENAME;
80         rn->private_data = pard = para_calloc(sizeof(*pard));
81         afhi = &pard->afhi;
82         ret = mmap_full_file(fn, O_RDONLY, &pard->map,
83                 &pard->map_size, &pard->fd);
84         if (ret < 0)
85                 goto out;
86         ret = compute_afhi(fn, pard->map, pard->map_size,
87                 pard->fd, afhi);
88         if (ret < 0)
89                 goto out_unmap;
90         pard->audio_format_num = ret;
91         ret = -ERRNO_TO_PARA_ERROR(EINVAL);
92         if (afhi->chunks_total == 0)
93                 goto out_clear_afhi;
94         if (PARA_ABS(bc) >= afhi->chunks_total)
95                 goto out_clear_afhi;
96         if (bc >= 0)
97                 pard->first_chunk = afh_get_start_chunk(bc, &pard->afhi);
98         else
99                 pard->first_chunk = afh_get_start_chunk(afhi->chunks_total + bc,
100                         &pard->afhi);
101         if (lls_opt_given(r_e)) {
102                 int32_t ec = lls_int32_val(0, r_e);
103                 ret = -ERRNO_TO_PARA_ERROR(EINVAL);
104                 if (PARA_ABS(ec) > afhi->chunks_total)
105                         goto out_clear_afhi;
106                 if (ec >= 0)
107                         pard->last_chunk = ec;
108                 else
109                         pard->last_chunk = afhi->chunks_total + ec;
110         } else
111                 pard->last_chunk = afhi->chunks_total - 1;
112         ret = -ERRNO_TO_PARA_ERROR(EINVAL);
113         if (pard->first_chunk >= pard->last_chunk)
114                 goto out_clear_afhi;
115         pard->current_chunk = pard->first_chunk;
116         return pard->audio_format_num;
117 out_clear_afhi:
118         clear_afhi(afhi);
119 out_unmap:
120         para_munmap(pard->map, pard->map_size);
121         close(pard->fd);
122 out:
123         freep(&rn->private_data);
124         return ret;
125 }
126
127 static void afh_recv_close(struct receiver_node *rn)
128 {
129         struct private_afh_recv_data *pard;
130
131         if (!rn || !rn->private_data)
132                 return;
133         pard = rn->private_data;
134         clear_afhi(&pard->afhi);
135         para_munmap(pard->map, pard->map_size);
136         close(pard->fd);
137         freep(&rn->private_data);
138 }
139
140 static void afh_recv_pre_select(struct sched *s, void *context)
141 {
142         struct receiver_node *rn = context;
143         struct private_afh_recv_data *pard = rn->private_data;
144         struct afh_info *afhi = &pard->afhi;
145         struct lls_parse_result *lpr = rn->lpr;
146         struct timeval chunk_time;
147         int state = generic_recv_pre_select(s, rn);
148         unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
149
150         if (state <= 0)
151                 return;
152         if (!j_given) {
153                 sched_min_delay(s);
154                 return;
155         }
156         compute_chunk_time(pard->current_chunk - pard->first_chunk,
157                 &afhi->chunk_tv, &pard->stream_start, &chunk_time);
158         sched_request_barrier_or_min_delay(&chunk_time, s);
159 }
160
161 static int afh_recv_post_select(__a_unused struct sched *s, void *context)
162 {
163         struct receiver_node *rn = context;
164         struct lls_parse_result *lpr = rn->lpr;
165         struct private_afh_recv_data *pard = rn->private_data;
166         struct btr_node *btrn = rn->btrn;
167         struct afh_info *afhi = &pard->afhi;
168         int ret;
169         char *buf;
170         const char *start, *end;
171         size_t size;
172         struct timeval chunk_time;
173         unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
174         unsigned H_given = RECV_CMD_OPT_GIVEN(AFH, NO_HEADER, lpr);
175
176         ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
177         if (ret <= 0)
178                 goto out;
179         if (pard->first_chunk > 0 && !H_given) {
180                 char *header;
181                 afh_get_header(afhi, pard->audio_format_num, pard->map,
182                         pard->map_size, &header, &size);
183                 if (size > 0) {
184                         PARA_INFO_LOG("writing header (%zu bytes)\n", size);
185                         buf = para_malloc(size);
186                         memcpy(buf, header, size);
187                         btr_add_output(buf, size, btrn);
188                         afh_free_header(header, pard->audio_format_num);
189                 }
190         }
191         if (!j_given) {
192                 afh_get_chunk(pard->first_chunk, afhi, pard->map, &start, &size);
193                 afh_get_chunk(pard->last_chunk, afhi, pard->map, &end, &size);
194                 end += size;
195                 PARA_INFO_LOG("adding %td bytes\n", end - start);
196                 btr_add_output_dont_free(start, end - start, btrn);
197                 ret = -E_RECV_EOF;
198                 goto out;
199         }
200         if (pard->current_chunk == pard->first_chunk)
201                 pard->stream_start = *now;
202         else {
203                 compute_chunk_time(pard->current_chunk - pard->first_chunk,
204                         &afhi->chunk_tv, &pard->stream_start, &chunk_time);
205                 ret = tv_diff(&chunk_time, now, NULL);
206                 if (ret > 0)
207                         goto out;
208         }
209         afh_get_chunk(pard->current_chunk, afhi, pard->map, &start, &size);
210         PARA_DEBUG_LOG("adding chunk %u\n", pard->current_chunk);
211         btr_add_output_dont_free(start, size, btrn);
212         if (pard->current_chunk >= pard->last_chunk) {
213                 ret = -E_RECV_EOF;
214                 goto out;
215         }
216         pard->current_chunk++;
217         ret = 1;
218 out:
219         if (ret < 0) {
220                 btr_remove_node(&rn->btrn);
221                 pard->current_chunk = pard->first_chunk;
222         }
223         return ret;
224 }
225
226 const struct receiver lsg_recv_cmd_com_afh_user_data = {
227         .init = afh_init,
228         .open = afh_recv_open,
229         .close = afh_recv_close,
230         .pre_select = afh_recv_pre_select,
231         .post_select = afh_recv_post_select,
232         .execute = afh_execute,
233 };