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