]> git.sesse.net Git - vlc/blob - modules/stream_out/cycle.c
sout: voidify sout_stream_t.pf_del
[vlc] / modules / stream_out / cycle.c
1 /*****************************************************************************
2  * cycle.c: cycle stream output module
3  *****************************************************************************
4  * Copyright (C) 2015 Rémi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Rémi Denis-Courmont reserves the right to redistribute this file under
12  * the terms of the GNU Lesser General Public License as published by the
13  * the Free Software Foundation; either version 2.1 or the License, or
14  * (at his option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #include <assert.h>
31 #include <stdlib.h>
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_block.h>
36 #include <vlc_sout.h>
37
38 typedef struct sout_cycle sout_cycle_t;
39
40 struct sout_cycle
41 {
42     sout_cycle_t *next;
43     mtime_t offset;
44     char chain[1];
45 };
46
47 struct sout_stream_id_sys_t
48 {
49     sout_stream_id_sys_t *prev;
50     sout_stream_id_sys_t *next;
51     es_format_t fmt;
52     void *id;
53 };
54
55 struct sout_stream_sys_t
56 {
57     sout_stream_t *stream; /*< Current output stream */
58     sout_stream_id_sys_t *first; /*< First elementary stream */
59     sout_stream_id_sys_t *last; /*< Last elementary stream */
60
61     sout_cycle_t *start;
62     sout_cycle_t *next;
63     mtime_t (*clock)(const block_t *);
64     mtime_t period; /*< Total cycle duration */
65 };
66
67 static mtime_t get_dts(const block_t *block)
68 {
69     return block->i_dts;
70 }
71
72 static sout_stream_id_sys_t *Add(sout_stream_t *stream, es_format_t *fmt)
73 {
74     sout_stream_sys_t *sys = stream->p_sys;
75     sout_stream_id_sys_t *id = malloc(sizeof (*id));
76     if (unlikely(id == NULL))
77         return NULL;
78
79     id->next = NULL;
80
81     if (es_format_Copy(&id->fmt, fmt))
82     {
83         free(id);
84         return NULL;
85     }
86
87     if (sys->stream != NULL)
88         id->id = sout_StreamIdAdd(sys->stream, &id->fmt);
89
90     id->prev = sys->last;
91     sys->last = id;
92     if (id->prev != NULL)
93         id->prev->next = id;
94     else
95         sys->first = id;
96     return id;
97 }
98
99 static void Del(sout_stream_t *stream, sout_stream_id_sys_t *id)
100 {
101     sout_stream_sys_t *sys = stream->p_sys;
102
103     if (id->prev != NULL)
104         id->prev->next = id->next;
105     else
106         sys->first = id->next;
107
108     if (id->next != NULL)
109         id->next->prev = id->prev;
110     else
111         sys->last = id->prev;
112
113     if (sys->stream != NULL)
114         sout_StreamIdDel(sys->stream, id->id);
115
116     es_format_Clean(&id->fmt);
117     free(id);
118 }
119
120 static int AddStream(sout_stream_t *stream, char *chain)
121 {
122     sout_stream_sys_t *sys = stream->p_sys;
123
124     msg_Dbg(stream, "starting new phase \"%s\"", chain);
125     /* TODO format */
126     sys->stream = sout_StreamChainNew(stream->p_sout, chain,
127                                       stream->p_next, NULL);
128     if (sys->stream == NULL)
129         return -1;
130
131     for (sout_stream_id_sys_t *id = sys->first; id != NULL; id = id->next)
132         id->id = sout_StreamIdAdd(sys->stream, &id->fmt);
133
134     return 0;
135 }
136
137 static void DelStream(sout_stream_t *stream)
138 {
139     sout_stream_sys_t *sys = stream->p_sys;
140
141     if (sys->stream == NULL)
142         return;
143
144     for (sout_stream_id_sys_t *id = sys->first; id != NULL; id = id->next)
145         if (id->id != NULL)
146             sout_StreamIdDel(sys->stream, id->id);
147
148     sout_StreamChainDelete(sys->stream, NULL);
149     sys->stream = NULL;
150 }
151
152 static int Send(sout_stream_t *stream, sout_stream_id_sys_t *id,
153                 block_t *block)
154 {
155     sout_stream_sys_t *sys = stream->p_sys;
156
157     for (block_t *next = block->p_next; block != NULL; block = next)
158     {
159         block->p_next = NULL;
160
161         /* FIXME: deal with key frames properly */
162         while (sys->clock(block) >= sys->next->offset)
163         {
164             DelStream(stream);
165             AddStream(stream, sys->next->chain);
166
167             sys->next->offset += sys->period;
168             sys->next = sys->next->next;
169             if (sys->next == NULL)
170                 sys->next = sys->start;
171         }
172
173         if (sys->stream != NULL)
174             sout_StreamIdSend(sys->stream, id->id, block);
175         else
176             block_Release(block);
177     }
178     return VLC_SUCCESS;
179 }
180
181 static int AppendPhase(sout_cycle_t ***restrict pp,
182                        mtime_t offset, const char *chain)
183 {
184
185     size_t len = strlen(chain);
186     sout_cycle_t *cycle = malloc(sizeof (*cycle) + len);
187     if (unlikely(cycle == NULL))
188         return -1;
189
190     cycle->next = NULL;
191     cycle->offset = offset;
192     memcpy(cycle->chain, chain, len + 1);
193
194     **pp = cycle;
195     *pp = &cycle->next;
196     return 0;
197 }
198
199 static mtime_t ParseTime(const char *str)
200 {
201     char *end;
202     unsigned long long u = strtoull(str, &end, 0);
203
204     switch (*end)
205     {
206         case 'w':
207             if (u > 15250284U)
208                 return -1;
209             return CLOCK_FREQ * 604800LLU * u;
210         case 'd':
211             if (u > 106751991U)
212                 return -1;
213             return CLOCK_FREQ * 86400LLU * u;
214         case 'h':
215             if (u > 2562047788U)
216                 return -1;
217             return CLOCK_FREQ * 3600LLU * u;
218         case 'm':
219             if (u > 153722867280U)
220                 return -1;
221             return CLOCK_FREQ * 60LLU * u;
222         case 's':
223         case 0:
224             if (u > 9223372036854U)
225                 return -1;
226             return CLOCK_FREQ * u;
227     }
228     return -1;
229 }
230
231 static int Open(vlc_object_t *obj)
232 {
233     sout_stream_t *stream = (sout_stream_t *)obj;
234     sout_stream_sys_t *sys = malloc(sizeof (*sys));
235     if (unlikely(sys == NULL))
236         return VLC_ENOMEM;
237
238     sys->stream = NULL;
239     sys->first = NULL;
240     sys->last = NULL;
241     sys->start = NULL;
242     sys->clock = get_dts;
243
244     mtime_t offset = 0;
245     sout_cycle_t **pp = &sys->start;
246     const char *chain = "";
247
248     for (const config_chain_t *cfg = stream->p_cfg;
249          cfg != NULL;
250          cfg = cfg->p_next)
251     {
252         if (!strcmp(cfg->psz_name, "dst"))
253         {
254             chain = cfg->psz_value;
255         }
256         else if (!strcmp(cfg->psz_name, "duration"))
257         {
258             mtime_t t = ParseTime(cfg->psz_value);
259
260             if (t > 0)
261             {
262                 AppendPhase(&pp, offset, chain);
263                 offset += t;
264             }
265
266             chain = "";
267         }
268         else if (!strcmp(cfg->psz_name, "offset"))
269         {
270             mtime_t t = ParseTime(cfg->psz_value);
271
272             if (t > offset)
273             {
274                 AppendPhase(&pp, offset, chain);
275                 offset = t;
276             }
277
278             chain = "";
279         }
280         else
281         {
282             msg_Err(stream, "unknown option \"%s\"", cfg->psz_name);
283         }
284     }
285
286     if (sys->start == NULL || offset <= 0)
287     {
288         free(sys);
289         msg_Err(stream, "unknown or invalid cycle specification");
290         return VLC_EGENERIC;
291     }
292
293     sys->next = sys->start;
294     sys->period = offset;
295
296     stream->pf_add = Add;
297     stream->pf_del = Del;
298     stream->pf_send = Send;
299     stream->p_sys = sys;
300     return VLC_SUCCESS;
301 }
302
303 static void Close(vlc_object_t *obj)
304 {
305     sout_stream_t *stream = (sout_stream_t *)obj;
306     sout_stream_sys_t *sys = stream->p_sys;
307
308     assert(sys->first == NULL && sys->last == NULL);
309
310     if (sys->stream != NULL)
311         sout_StreamChainDelete(sys->stream, NULL);
312
313     for (sout_cycle_t *cycle = sys->start, *next; cycle != NULL; cycle = next)
314     {
315         next = cycle->next;
316         free(cycle);
317     }
318
319     free(sys);
320 }
321
322 vlc_module_begin()
323     set_description(N_("Cyclic stream output"))
324     set_capability("sout stream", 0)
325     set_category(CAT_SOUT)
326     set_subcategory(SUBCAT_SOUT_STREAM)
327     set_callbacks(Open, Close)
328 vlc_module_end()