1 /*****************************************************************************
2 * cycle.c: cycle stream output module
3 *****************************************************************************
4 * Copyright (C) 2015 Rémi Denis-Courmont
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.
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.
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.
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 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_block.h>
38 typedef struct sout_cycle sout_cycle_t;
47 struct sout_stream_id_sys_t
49 sout_stream_id_sys_t *prev;
50 sout_stream_id_sys_t *next;
55 struct sout_stream_sys_t
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 */
63 mtime_t (*clock)(const block_t *);
64 mtime_t period; /*< Total cycle duration */
67 static mtime_t get_dts(const block_t *block)
72 static sout_stream_id_sys_t *Add(sout_stream_t *stream, es_format_t *fmt)
74 sout_stream_sys_t *sys = stream->p_sys;
75 sout_stream_id_sys_t *id = malloc(sizeof (*id));
76 if (unlikely(id == NULL))
81 if (es_format_Copy(&id->fmt, fmt))
87 if (sys->stream != NULL)
88 id->id = sout_StreamIdAdd(sys->stream, &id->fmt);
99 static void Del(sout_stream_t *stream, sout_stream_id_sys_t *id)
101 sout_stream_sys_t *sys = stream->p_sys;
103 if (id->prev != NULL)
104 id->prev->next = id->next;
106 sys->first = id->next;
108 if (id->next != NULL)
109 id->next->prev = id->prev;
111 sys->last = id->prev;
113 if (sys->stream != NULL)
114 sout_StreamIdDel(sys->stream, id->id);
116 es_format_Clean(&id->fmt);
120 static int AddStream(sout_stream_t *stream, char *chain)
122 sout_stream_sys_t *sys = stream->p_sys;
124 msg_Dbg(stream, "starting new phase \"%s\"", chain);
126 sys->stream = sout_StreamChainNew(stream->p_sout, chain,
127 stream->p_next, NULL);
128 if (sys->stream == NULL)
131 for (sout_stream_id_sys_t *id = sys->first; id != NULL; id = id->next)
132 id->id = sout_StreamIdAdd(sys->stream, &id->fmt);
137 static void DelStream(sout_stream_t *stream)
139 sout_stream_sys_t *sys = stream->p_sys;
141 if (sys->stream == NULL)
144 for (sout_stream_id_sys_t *id = sys->first; id != NULL; id = id->next)
146 sout_StreamIdDel(sys->stream, id->id);
148 sout_StreamChainDelete(sys->stream, NULL);
152 static int Send(sout_stream_t *stream, sout_stream_id_sys_t *id,
155 sout_stream_sys_t *sys = stream->p_sys;
157 for (block_t *next = block->p_next; block != NULL; block = next)
159 block->p_next = NULL;
161 /* FIXME: deal with key frames properly */
162 while (sys->clock(block) >= sys->next->offset)
165 AddStream(stream, sys->next->chain);
167 sys->next->offset += sys->period;
168 sys->next = sys->next->next;
169 if (sys->next == NULL)
170 sys->next = sys->start;
173 if (sys->stream != NULL)
174 sout_StreamIdSend(sys->stream, id->id, block);
176 block_Release(block);
181 static int AppendPhase(sout_cycle_t ***restrict pp,
182 mtime_t offset, const char *chain)
185 size_t len = strlen(chain);
186 sout_cycle_t *cycle = malloc(sizeof (*cycle) + len);
187 if (unlikely(cycle == NULL))
191 cycle->offset = offset;
192 memcpy(cycle->chain, chain, len + 1);
199 static mtime_t ParseTime(const char *str)
202 unsigned long long u = strtoull(str, &end, 0);
209 return CLOCK_FREQ * 604800LLU * u;
213 return CLOCK_FREQ * 86400LLU * u;
217 return CLOCK_FREQ * 3600LLU * u;
219 if (u > 153722867280U)
221 return CLOCK_FREQ * 60LLU * u;
224 if (u > 9223372036854U)
226 return CLOCK_FREQ * u;
231 static int Open(vlc_object_t *obj)
233 sout_stream_t *stream = (sout_stream_t *)obj;
234 sout_stream_sys_t *sys = malloc(sizeof (*sys));
235 if (unlikely(sys == NULL))
242 sys->clock = get_dts;
245 sout_cycle_t **pp = &sys->start;
246 const char *chain = "";
248 for (const config_chain_t *cfg = stream->p_cfg;
252 if (!strcmp(cfg->psz_name, "dst"))
254 chain = cfg->psz_value;
256 else if (!strcmp(cfg->psz_name, "duration"))
258 mtime_t t = ParseTime(cfg->psz_value);
262 AppendPhase(&pp, offset, chain);
268 else if (!strcmp(cfg->psz_name, "offset"))
270 mtime_t t = ParseTime(cfg->psz_value);
274 AppendPhase(&pp, offset, chain);
282 msg_Err(stream, "unknown option \"%s\"", cfg->psz_name);
286 if (sys->start == NULL || offset <= 0)
289 msg_Err(stream, "unknown or invalid cycle specification");
293 sys->next = sys->start;
294 sys->period = offset;
296 stream->pf_add = Add;
297 stream->pf_del = Del;
298 stream->pf_send = Send;
303 static void Close(vlc_object_t *obj)
305 sout_stream_t *stream = (sout_stream_t *)obj;
306 sout_stream_sys_t *sys = stream->p_sys;
308 assert(sys->first == NULL && sys->last == NULL);
310 if (sys->stream != NULL)
311 sout_StreamChainDelete(sys->stream, NULL);
313 for (sout_cycle_t *cycle = sys->start, *next; cycle != NULL; cycle = next)
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)