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 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_block.h>
39 typedef struct sout_cycle sout_cycle_t
;
48 struct sout_stream_id_sys_t
50 sout_stream_id_sys_t
*prev
;
51 sout_stream_id_sys_t
*next
;
56 struct sout_stream_sys_t
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 */
64 mtime_t (*clock
)(const block_t
*);
65 mtime_t period
; /*< Total cycle duration */
68 static mtime_t
get_dts(const block_t
*block
)
73 static sout_stream_id_sys_t
*Add(sout_stream_t
*stream
, const es_format_t
*fmt
)
75 sout_stream_sys_t
*sys
= stream
->p_sys
;
76 sout_stream_id_sys_t
*id
= malloc(sizeof (*id
));
77 if (unlikely(id
== NULL
))
82 if (es_format_Copy(&id
->fmt
, fmt
))
84 es_format_Clean(&id
->fmt
);
89 if (sys
->stream
!= NULL
)
90 id
->id
= sout_StreamIdAdd(sys
->stream
, &id
->fmt
);
101 static void Del(sout_stream_t
*stream
, sout_stream_id_sys_t
*id
)
103 sout_stream_sys_t
*sys
= stream
->p_sys
;
105 if (id
->prev
!= NULL
)
106 id
->prev
->next
= id
->next
;
108 sys
->first
= id
->next
;
110 if (id
->next
!= NULL
)
111 id
->next
->prev
= id
->prev
;
113 sys
->last
= id
->prev
;
115 if (sys
->stream
!= NULL
)
116 sout_StreamIdDel(sys
->stream
, id
->id
);
118 es_format_Clean(&id
->fmt
);
122 static int AddStream(sout_stream_t
*stream
, char *chain
)
124 sout_stream_sys_t
*sys
= stream
->p_sys
;
126 msg_Dbg(stream
, "starting new phase \"%s\"", chain
);
128 sys
->stream
= sout_StreamChainNew(stream
->p_sout
, chain
,
129 stream
->p_next
, NULL
);
130 if (sys
->stream
== NULL
)
133 for (sout_stream_id_sys_t
*id
= sys
->first
; id
!= NULL
; id
= id
->next
)
134 id
->id
= sout_StreamIdAdd(sys
->stream
, &id
->fmt
);
139 static void DelStream(sout_stream_t
*stream
)
141 sout_stream_sys_t
*sys
= stream
->p_sys
;
143 if (sys
->stream
== NULL
)
146 for (sout_stream_id_sys_t
*id
= sys
->first
; id
!= NULL
; id
= id
->next
)
148 sout_StreamIdDel(sys
->stream
, id
->id
);
150 sout_StreamChainDelete(sys
->stream
, NULL
);
154 static int Send(sout_stream_t
*stream
, sout_stream_id_sys_t
*id
,
157 sout_stream_sys_t
*sys
= stream
->p_sys
;
159 for (block_t
*next
= block
->p_next
; block
!= NULL
; block
= next
)
161 block
->p_next
= NULL
;
163 /* FIXME: deal with key frames properly */
164 while (sys
->clock(block
) >= sys
->next
->offset
)
167 AddStream(stream
, sys
->next
->chain
);
169 sys
->next
->offset
+= sys
->period
;
170 sys
->next
= sys
->next
->next
;
171 if (sys
->next
== NULL
)
172 sys
->next
= sys
->start
;
175 if (sys
->stream
!= NULL
)
176 sout_StreamIdSend(sys
->stream
, id
->id
, block
);
178 block_Release(block
);
183 static int AppendPhase(sout_cycle_t
***restrict pp
,
184 mtime_t offset
, const char *chain
)
187 size_t len
= strlen(chain
);
188 sout_cycle_t
*cycle
= malloc(sizeof (*cycle
) + len
);
189 if (unlikely(cycle
== NULL
))
193 cycle
->offset
= offset
;
194 memcpy(cycle
->chain
, chain
, len
+ 1);
201 static mtime_t
ParseTime(const char *str
)
204 unsigned long long u
= strtoull(str
, &end
, 0);
211 return CLOCK_FREQ
* 604800LLU * u
;
215 return CLOCK_FREQ
* 86400LLU * u
;
219 return CLOCK_FREQ
* 3600LLU * u
;
221 if (u
> 153722867280U)
223 return CLOCK_FREQ
* 60LLU * u
;
226 if (u
> 9223372036854U)
228 return CLOCK_FREQ
* u
;
233 static int Open(vlc_object_t
*obj
)
235 sout_stream_t
*stream
= (sout_stream_t
*)obj
;
236 sout_stream_sys_t
*sys
= malloc(sizeof (*sys
));
237 if (unlikely(sys
== NULL
))
244 sys
->clock
= get_dts
;
247 sout_cycle_t
**pp
= &sys
->start
;
248 const char *chain
= "";
250 for (const config_chain_t
*cfg
= stream
->p_cfg
;
254 if (!strcmp(cfg
->psz_name
, "dst"))
256 chain
= cfg
->psz_value
;
258 else if (!strcmp(cfg
->psz_name
, "duration"))
260 mtime_t t
= ParseTime(cfg
->psz_value
);
264 AppendPhase(&pp
, offset
, chain
);
270 else if (!strcmp(cfg
->psz_name
, "offset"))
272 mtime_t t
= ParseTime(cfg
->psz_value
);
276 AppendPhase(&pp
, offset
, chain
);
284 msg_Err(stream
, "unknown option \"%s\"", cfg
->psz_name
);
288 if (sys
->start
== NULL
|| offset
<= 0)
291 msg_Err(stream
, "unknown or invalid cycle specification");
295 sys
->next
= sys
->start
;
296 sys
->period
= offset
;
298 stream
->pf_add
= Add
;
299 stream
->pf_del
= Del
;
300 stream
->pf_send
= Send
;
305 static void Close(vlc_object_t
*obj
)
307 sout_stream_t
*stream
= (sout_stream_t
*)obj
;
308 sout_stream_sys_t
*sys
= stream
->p_sys
;
310 assert(sys
->first
== NULL
&& sys
->last
== NULL
);
312 if (sys
->stream
!= NULL
)
313 sout_StreamChainDelete(sys
->stream
, NULL
);
315 for (sout_cycle_t
*cycle
= sys
->start
, *next
; cycle
!= NULL
; cycle
= next
)
325 set_shortname(N_("cycle"))
326 set_description(N_("Cyclic stream output"))
327 set_capability("sout stream", 0)
328 set_category(CAT_SOUT
)
329 set_subcategory(SUBCAT_SOUT_STREAM
)
330 set_callbacks(Open
, Close
)
331 add_shortcut("cycle")