]> git.sesse.net Git - vlc/blob - modules/stream_out/cycle.c
9678bc55910c9b42b3a3aa4b527ce1858772f747
[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, const 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         es_format_Clean(&id->fmt);
84         free(id);
85         return NULL;
86     }
87
88     if (sys->stream != NULL)
89         id->id = sout_StreamIdAdd(sys->stream, &id->fmt);
90
91     id->prev = sys->last;
92     sys->last = id;
93     if (id->prev != NULL)
94         id->prev->next = id;
95     else
96         sys->first = id;
97     return id;
98 }
99
100 static void Del(sout_stream_t *stream, sout_stream_id_sys_t *id)
101 {
102     sout_stream_sys_t *sys = stream->p_sys;
103
104     if (id->prev != NULL)
105         id->prev->next = id->next;
106     else
107         sys->first = id->next;
108
109     if (id->next != NULL)
110         id->next->prev = id->prev;
111     else
112         sys->last = id->prev;
113
114     if (sys->stream != NULL)
115         sout_StreamIdDel(sys->stream, id->id);
116
117     es_format_Clean(&id->fmt);
118     free(id);
119 }
120
121 static int AddStream(sout_stream_t *stream, char *chain)
122 {
123     sout_stream_sys_t *sys = stream->p_sys;
124
125     msg_Dbg(stream, "starting new phase \"%s\"", chain);
126     /* TODO format */
127     sys->stream = sout_StreamChainNew(stream->p_sout, chain,
128                                       stream->p_next, NULL);
129     if (sys->stream == NULL)
130         return -1;
131
132     for (sout_stream_id_sys_t *id = sys->first; id != NULL; id = id->next)
133         id->id = sout_StreamIdAdd(sys->stream, &id->fmt);
134
135     return 0;
136 }
137
138 static void DelStream(sout_stream_t *stream)
139 {
140     sout_stream_sys_t *sys = stream->p_sys;
141
142     if (sys->stream == NULL)
143         return;
144
145     for (sout_stream_id_sys_t *id = sys->first; id != NULL; id = id->next)
146         if (id->id != NULL)
147             sout_StreamIdDel(sys->stream, id->id);
148
149     sout_StreamChainDelete(sys->stream, NULL);
150     sys->stream = NULL;
151 }
152
153 static int Send(sout_stream_t *stream, sout_stream_id_sys_t *id,
154                 block_t *block)
155 {
156     sout_stream_sys_t *sys = stream->p_sys;
157
158     for (block_t *next = block->p_next; block != NULL; block = next)
159     {
160         block->p_next = NULL;
161
162         /* FIXME: deal with key frames properly */
163         while (sys->clock(block) >= sys->next->offset)
164         {
165             DelStream(stream);
166             AddStream(stream, sys->next->chain);
167
168             sys->next->offset += sys->period;
169             sys->next = sys->next->next;
170             if (sys->next == NULL)
171                 sys->next = sys->start;
172         }
173
174         if (sys->stream != NULL)
175             sout_StreamIdSend(sys->stream, id->id, block);
176         else
177             block_Release(block);
178     }
179     return VLC_SUCCESS;
180 }
181
182 static int AppendPhase(sout_cycle_t ***restrict pp,
183                        mtime_t offset, const char *chain)
184 {
185
186     size_t len = strlen(chain);
187     sout_cycle_t *cycle = malloc(sizeof (*cycle) + len);
188     if (unlikely(cycle == NULL))
189         return -1;
190
191     cycle->next = NULL;
192     cycle->offset = offset;
193     memcpy(cycle->chain, chain, len + 1);
194
195     **pp = cycle;
196     *pp = &cycle->next;
197     return 0;
198 }
199
200 static mtime_t ParseTime(const char *str)
201 {
202     char *end;
203     unsigned long long u = strtoull(str, &end, 0);
204
205     switch (*end)
206     {
207         case 'w':
208             if (u > 15250284U)
209                 return -1;
210             return CLOCK_FREQ * 604800LLU * u;
211         case 'd':
212             if (u > 106751991U)
213                 return -1;
214             return CLOCK_FREQ * 86400LLU * u;
215         case 'h':
216             if (u > 2562047788U)
217                 return -1;
218             return CLOCK_FREQ * 3600LLU * u;
219         case 'm':
220             if (u > 153722867280U)
221                 return -1;
222             return CLOCK_FREQ * 60LLU * u;
223         case 's':
224         case 0:
225             if (u > 9223372036854U)
226                 return -1;
227             return CLOCK_FREQ * u;
228     }
229     return -1;
230 }
231
232 static int Open(vlc_object_t *obj)
233 {
234     sout_stream_t *stream = (sout_stream_t *)obj;
235     sout_stream_sys_t *sys = malloc(sizeof (*sys));
236     if (unlikely(sys == NULL))
237         return VLC_ENOMEM;
238
239     sys->stream = NULL;
240     sys->first = NULL;
241     sys->last = NULL;
242     sys->start = NULL;
243     sys->clock = get_dts;
244
245     mtime_t offset = 0;
246     sout_cycle_t **pp = &sys->start;
247     const char *chain = "";
248
249     for (const config_chain_t *cfg = stream->p_cfg;
250          cfg != NULL;
251          cfg = cfg->p_next)
252     {
253         if (!strcmp(cfg->psz_name, "dst"))
254         {
255             chain = cfg->psz_value;
256         }
257         else if (!strcmp(cfg->psz_name, "duration"))
258         {
259             mtime_t t = ParseTime(cfg->psz_value);
260
261             if (t > 0)
262             {
263                 AppendPhase(&pp, offset, chain);
264                 offset += t;
265             }
266
267             chain = "";
268         }
269         else if (!strcmp(cfg->psz_name, "offset"))
270         {
271             mtime_t t = ParseTime(cfg->psz_value);
272
273             if (t > offset)
274             {
275                 AppendPhase(&pp, offset, chain);
276                 offset = t;
277             }
278
279             chain = "";
280         }
281         else
282         {
283             msg_Err(stream, "unknown option \"%s\"", cfg->psz_name);
284         }
285     }
286
287     if (sys->start == NULL || offset <= 0)
288     {
289         free(sys);
290         msg_Err(stream, "unknown or invalid cycle specification");
291         return VLC_EGENERIC;
292     }
293
294     sys->next = sys->start;
295     sys->period = offset;
296
297     stream->pf_add = Add;
298     stream->pf_del = Del;
299     stream->pf_send = Send;
300     stream->p_sys = sys;
301     return VLC_SUCCESS;
302 }
303
304 static void Close(vlc_object_t *obj)
305 {
306     sout_stream_t *stream = (sout_stream_t *)obj;
307     sout_stream_sys_t *sys = stream->p_sys;
308
309     assert(sys->first == NULL && sys->last == NULL);
310
311     if (sys->stream != NULL)
312         sout_StreamChainDelete(sys->stream, NULL);
313
314     for (sout_cycle_t *cycle = sys->start, *next; cycle != NULL; cycle = next)
315     {
316         next = cycle->next;
317         free(cycle);
318     }
319
320     free(sys);
321 }
322
323 vlc_module_begin()
324     set_description(N_("Cyclic stream output"))
325     set_capability("sout stream", 0)
326     set_category(CAT_SOUT)
327     set_subcategory(SUBCAT_SOUT_STREAM)
328     set_callbacks(Open, Close)
329 vlc_module_end()