wma_afh: Add some assert() statements.
[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         void *afh_context;
35 };
36
37 static int afh_execute(struct btr_node *btrn, const char *cmd, char **result)
38 {
39         struct receiver_node *rn = btr_context(btrn);
40         struct private_afh_recv_data *pard = rn->private_data;
41
42         *result = NULL;
43         if (!strcmp(cmd, "seconds_total")) {
44                 *result = make_message("%" PRIu32, pard->afhi.seconds_total);
45                 return 1;
46         }
47         if (!strcmp(cmd, "chunks_total")) {
48                 *result = make_message("%" PRIu32, pard->afhi.chunks_total);
49                 return 1;
50         }
51         if (!strcmp(cmd, "afhi")) {
52                 afh_get_afhi_txt(pard->audio_format_num, &pard->afhi, result);
53                 return 1;
54         }
55         if (!strncmp(cmd, "repos", 5)) {
56                 int32_t x;
57                 int ret = para_atoi32(cmd + 5, &x);
58                 if (ret < 0)
59                         return ret;
60                 if (x >= pard->afhi.chunks_total)
61                         return -ERRNO_TO_PARA_ERROR(EINVAL);
62                 pard->first_chunk = afh_get_start_chunk(x, &pard->afhi,
63                         pard->audio_format_num);
64                 pard->current_chunk = pard->first_chunk;
65                 return 1;
66         }
67         return -E_BTR_NAVAIL;
68 }
69
70 static int afh_recv_open(struct receiver_node *rn)
71 {
72         struct lls_parse_result *lpr = rn->lpr;
73         struct private_afh_recv_data *pard;
74         struct afh_info *afhi;
75         const char *fn = RECV_CMD_OPT_STRING_VAL(AFH, FILENAME, lpr);
76         int32_t bc = RECV_CMD_OPT_INT32_VAL(AFH, BEGIN_CHUNK, lpr);
77         const struct lls_opt_result *r_e = RECV_CMD_OPT_RESULT(AFH, END_CHUNK, lpr);
78         int ret;
79
80         if (!fn || *fn == '\0')
81                 return -E_AFH_RECV_BAD_FILENAME;
82         rn->private_data = pard = para_calloc(sizeof(*pard));
83         afhi = &pard->afhi;
84         ret = mmap_full_file(fn, O_RDONLY, &pard->map,
85                 &pard->map_size, &pard->fd);
86         if (ret < 0)
87                 goto out;
88         ret = compute_afhi(fn, pard->map, pard->map_size,
89                 pard->fd, afhi);
90         if (ret < 0)
91                 goto out_unmap;
92         pard->audio_format_num = ret;
93         ret = -ERRNO_TO_PARA_ERROR(EINVAL);
94         if (afhi->chunks_total == 0)
95                 goto out_clear_afhi;
96         if (PARA_ABS(bc) >= afhi->chunks_total)
97                 goto out_clear_afhi;
98         if (bc >= 0)
99                 pard->first_chunk = afh_get_start_chunk(bc, &pard->afhi,
100                         pard->audio_format_num);
101         else
102                 pard->first_chunk = afh_get_start_chunk(afhi->chunks_total + bc,
103                         &pard->afhi, pard->audio_format_num);
104         if (lls_opt_given(r_e)) {
105                 int32_t ec = lls_int32_val(0, r_e);
106                 ret = -ERRNO_TO_PARA_ERROR(EINVAL);
107                 if (PARA_ABS(ec) > afhi->chunks_total)
108                         goto out_clear_afhi;
109                 if (ec >= 0)
110                         pard->last_chunk = ec;
111                 else
112                         pard->last_chunk = afhi->chunks_total + ec;
113         } else
114                 pard->last_chunk = afhi->chunks_total - 1;
115         ret = -ERRNO_TO_PARA_ERROR(EINVAL);
116         if (pard->first_chunk >= pard->last_chunk)
117                 goto out_clear_afhi;
118         pard->current_chunk = pard->first_chunk;
119         return pard->audio_format_num;
120 out_clear_afhi:
121         clear_afhi(afhi);
122 out_unmap:
123         para_munmap(pard->map, pard->map_size);
124         close(pard->fd);
125 out:
126         freep(&rn->private_data);
127         return ret;
128 }
129
130 static void afh_recv_close(struct receiver_node *rn)
131 {
132         struct private_afh_recv_data *pard;
133
134         if (!rn || !rn->private_data)
135                 return;
136         pard = rn->private_data;
137         clear_afhi(&pard->afhi);
138         para_munmap(pard->map, pard->map_size);
139         close(pard->fd);
140         afh_close(pard->afh_context, pard->audio_format_num);
141         freep(&rn->private_data);
142 }
143
144 static void afh_recv_pre_select(struct sched *s, void *context)
145 {
146         struct receiver_node *rn = context;
147         struct private_afh_recv_data *pard = rn->private_data;
148         struct afh_info *afhi = &pard->afhi;
149         struct lls_parse_result *lpr = rn->lpr;
150         struct timeval chunk_time;
151         int state = generic_recv_pre_select(s, rn);
152         unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
153
154         if (state <= 0)
155                 return;
156         if (!j_given) {
157                 sched_min_delay(s);
158                 return;
159         }
160         compute_chunk_time(pard->current_chunk - pard->first_chunk,
161                 &afhi->chunk_tv, &pard->stream_start, &chunk_time);
162         sched_request_barrier_or_min_delay(&chunk_time, s);
163 }
164
165 static int afh_recv_post_select(__a_unused struct sched *s, void *context)
166 {
167         struct receiver_node *rn = context;
168         struct lls_parse_result *lpr = rn->lpr;
169         struct private_afh_recv_data *pard = rn->private_data;
170         struct btr_node *btrn = rn->btrn;
171         struct afh_info *afhi = &pard->afhi;
172         int ret;
173         char *buf;
174         const char *start;
175         size_t size;
176         struct timeval chunk_time;
177         unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
178         unsigned H_given = RECV_CMD_OPT_GIVEN(AFH, NO_HEADER, lpr);
179
180         ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
181         if (ret <= 0)
182                 goto out;
183         if (pard->first_chunk > 0 && !H_given) {
184                 char *header;
185                 afh_get_header(afhi, pard->audio_format_num, pard->map,
186                         pard->map_size, &header, &size);
187                 if (size > 0) {
188                         PARA_INFO_LOG("writing header (%zu bytes)\n", size);
189                         buf = para_malloc(size);
190                         memcpy(buf, header, size);
191                         btr_add_output(buf, size, btrn);
192                         afh_free_header(header, pard->audio_format_num);
193                 }
194         }
195         if (!j_given) {
196                 long unsigned n;
197                 for (n = pard->first_chunk; n < pard->last_chunk; n++) {
198                         ret = afh_get_chunk(n, afhi, pard->audio_format_num,
199                                 pard->map, pard->map_size, &start, &size,
200                                 &pard->afh_context);
201                         if (ret < 0)
202                                 goto out;
203                         PARA_INFO_LOG("adding %zu bytes\n", size);
204                         btr_add_output_dont_free(start, size, btrn);
205                 }
206                 ret = -E_RECV_EOF;
207                 goto out;
208         }
209         if (pard->current_chunk == pard->first_chunk)
210                 pard->stream_start = *now;
211         else {
212                 compute_chunk_time(pard->current_chunk - pard->first_chunk,
213                         &afhi->chunk_tv, &pard->stream_start, &chunk_time);
214                 ret = tv_diff(&chunk_time, now, NULL);
215                 if (ret > 0)
216                         goto out;
217         }
218         ret = afh_get_chunk(pard->current_chunk, afhi,
219                 pard->audio_format_num, pard->map,
220                 pard->map_size, &start, &size,
221                 &pard->afh_context);
222         if (ret < 0)
223                 goto out;
224         PARA_DEBUG_LOG("adding chunk %u\n", pard->current_chunk);
225         btr_add_output_dont_free(start, size, btrn);
226         if (pard->current_chunk >= pard->last_chunk) {
227                 ret = -E_RECV_EOF;
228                 goto out;
229         }
230         pard->current_chunk++;
231         ret = 1;
232 out:
233         if (ret < 0) {
234                 btr_remove_node(&rn->btrn);
235                 pard->current_chunk = pard->first_chunk;
236         }
237         return ret;
238 }
239
240 /** See \ref recv_init(). */
241 const struct receiver lsg_recv_cmd_com_afh_user_data = {
242         .init = afh_init,
243         .open = afh_recv_open,
244         .close = afh_recv_close,
245         .pre_select = afh_recv_pre_select,
246         .post_select = afh_recv_post_select,
247         .execute = afh_execute,
248 };