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, const 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))
83 es_format_Clean(&id->fmt);
88 if (sys->stream != NULL)
89 id->id = sout_StreamIdAdd(sys->stream, &id->fmt);
100 static void Del(sout_stream_t *stream, sout_stream_id_sys_t *id)
102 sout_stream_sys_t *sys = stream->p_sys;
104 if (id->prev != NULL)
105 id->prev->next = id->next;
107 sys->first = id->next;
109 if (id->next != NULL)
110 id->next->prev = id->prev;
112 sys->last = id->prev;
114 if (sys->stream != NULL)
115 sout_StreamIdDel(sys->stream, id->id);
117 es_format_Clean(&id->fmt);
121 static int AddStream(sout_stream_t *stream, char *chain)
123 sout_stream_sys_t *sys = stream->p_sys;
125 msg_Dbg(stream, "starting new phase \"%s\"", chain);
127 sys->stream = sout_StreamChainNew(stream->p_sout, chain,
128 stream->p_next, NULL);
129 if (sys->stream == NULL)
132 for (sout_stream_id_sys_t *id = sys->first; id != NULL; id = id->next)
133 id->id = sout_StreamIdAdd(sys->stream, &id->fmt);
138 static void DelStream(sout_stream_t *stream)
140 sout_stream_sys_t *sys = stream->p_sys;
142 if (sys->stream == NULL)
145 for (sout_stream_id_sys_t *id = sys->first; id != NULL; id = id->next)
147 sout_StreamIdDel(sys->stream, id->id);
149 sout_StreamChainDelete(sys->stream, NULL);
153 static int Send(sout_stream_t *stream, sout_stream_id_sys_t *id,
156 sout_stream_sys_t *sys = stream->p_sys;
158 for (block_t *next = block->p_next; block != NULL; block = next)
160 block->p_next = NULL;
162 /* FIXME: deal with key frames properly */
163 while (sys->clock(block) >= sys->next->offset)
166 AddStream(stream, sys->next->chain);
168 sys->next->offset += sys->period;
169 sys->next = sys->next->next;
170 if (sys->next == NULL)
171 sys->next = sys->start;
174 if (sys->stream != NULL)
175 sout_StreamIdSend(sys->stream, id->id, block);
177 block_Release(block);
182 static int AppendPhase(sout_cycle_t ***restrict pp,
183 mtime_t offset, const char *chain)
186 size_t len = strlen(chain);
187 sout_cycle_t *cycle = malloc(sizeof (*cycle) + len);
188 if (unlikely(cycle == NULL))
192 cycle->offset = offset;
193 memcpy(cycle->chain, chain, len + 1);
200 static mtime_t ParseTime(const char *str)
203 unsigned long long u = strtoull(str, &end, 0);
210 return CLOCK_FREQ * 604800LLU * u;
214 return CLOCK_FREQ * 86400LLU * u;
218 return CLOCK_FREQ * 3600LLU * u;
220 if (u > 153722867280U)
222 return CLOCK_FREQ * 60LLU * u;
225 if (u > 9223372036854U)
227 return CLOCK_FREQ * u;
232 static int Open(vlc_object_t *obj)
234 sout_stream_t *stream = (sout_stream_t *)obj;
235 sout_stream_sys_t *sys = malloc(sizeof (*sys));
236 if (unlikely(sys == NULL))
243 sys->clock = get_dts;
246 sout_cycle_t **pp = &sys->start;
247 const char *chain = "";
249 for (const config_chain_t *cfg = stream->p_cfg;
253 if (!strcmp(cfg->psz_name, "dst"))
255 chain = cfg->psz_value;
257 else if (!strcmp(cfg->psz_name, "duration"))
259 mtime_t t = ParseTime(cfg->psz_value);
263 AppendPhase(&pp, offset, chain);
269 else if (!strcmp(cfg->psz_name, "offset"))
271 mtime_t t = ParseTime(cfg->psz_value);
275 AppendPhase(&pp, offset, chain);
283 msg_Err(stream, "unknown option \"%s\"", cfg->psz_name);
287 if (sys->start == NULL || offset <= 0)
290 msg_Err(stream, "unknown or invalid cycle specification");
294 sys->next = sys->start;
295 sys->period = offset;
297 stream->pf_add = Add;
298 stream->pf_del = Del;
299 stream->pf_send = Send;
304 static void Close(vlc_object_t *obj)
306 sout_stream_t *stream = (sout_stream_t *)obj;
307 sout_stream_sys_t *sys = stream->p_sys;
309 assert(sys->first == NULL && sys->last == NULL);
311 if (sys->stream != NULL)
312 sout_StreamChainDelete(sys->stream, NULL);
314 for (sout_cycle_t *cycle = sys->start, *next; cycle != NULL; cycle = next)
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)