1 /*
2 * Digital Video Recorder
3 * Copyright (C) 2008 Andreas Öman
4 * Copyright (C) 2014,2015 Jaroslav Kysela
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 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <stdarg.h>
21 #include <pthread.h>
22 #include <assert.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <sys/stat.h>
26 #include <libgen.h> /* basename */
27
28 #include "htsstr.h"
29
30 #include "tvheadend.h"
31 #include "streaming.h"
32 #include "tcp.h"
33 #include "dvr.h"
34 #include "spawn.h"
35 #include "service.h"
36 #include "htsp_server.h"
37 #include "atomic.h"
38 #include "intlconv.h"
39 #include "notify.h"
40
41 #include "muxer.h"
42
43 /**
44 *
45 */
46 static void *dvr_thread(void *aux);
47 static void dvr_thread_epilog(dvr_entry_t *de, const char *dvr_postproc);
48
49
50 static const int prio2weight[6] = {
51 [DVR_PRIO_IMPORTANT] = 500,
52 [DVR_PRIO_HIGH] = 400,
53 [DVR_PRIO_NORMAL] = 300,
54 [DVR_PRIO_LOW] = 200,
55 [DVR_PRIO_UNIMPORTANT] = 100,
56 [DVR_PRIO_NOTSET] = 300, /* as DVR_PRIO_NORMAL */
57 };
58
59 /**
60 *
61 */
62 int
dvr_rec_subscribe(dvr_entry_t * de)63 dvr_rec_subscribe(dvr_entry_t *de)
64 {
65 char buf[100];
66 int weight;
67 profile_t *pro;
68 profile_chain_t *prch = NULL;
69 struct sockaddr_storage sa;
70 access_t *aa = NULL;
71 uint32_t rec_count, net_count;
72 int ret = 0, c1, c2;
73 struct stat st;
74
75 assert(de->de_s == NULL);
76 assert(de->de_chain == NULL);
77
78 if(de->de_pri >= 0 && de->de_pri < ARRAY_SIZE(prio2weight))
79 weight = prio2weight[de->de_pri];
80 else
81 weight = 300;
82
83 snprintf(buf, sizeof(buf), "DVR: %s", lang_str_get(de->de_title, NULL));
84
85 if (de->de_owner && de->de_owner[0] != '\0') {
86 aa = access_get_by_username(de->de_owner);
87 } else if (de->de_creator && de->de_creator[0] != '\0' &&
88 tcp_get_ip_from_str(de->de_creator, &sa) != NULL) {
89 aa = access_get_by_addr(&sa);
90 } else {
91 tvherror(LS_DVR, "unable to find access (owner '%s', creator '%s')",
92 de->de_owner, de->de_creator);
93 ret = -EPERM;
94 goto _return;
95 }
96
97 if (aa->aa_conn_limit || aa->aa_conn_limit_dvr) {
98 rec_count = dvr_usage_count(aa);
99 net_count = aa->aa_conn_limit ? tcp_connection_count(aa) : 0;
100 /* the rule is: allow if one condition is OK */
101 c1 = aa->aa_conn_limit ? rec_count + net_count >= aa->aa_conn_limit : -1;
102 c2 = aa->aa_conn_limit_dvr ? rec_count >= aa->aa_conn_limit_dvr : -1;
103 if (c1 && c2) {
104 tvherror(LS_DVR, "multiple connections are not allowed for user '%s' from '%s' "
105 "(limit %u, dvr limit %u, active DVR %u, streaming %u)",
106 aa->aa_username ?: "", aa->aa_representative ?: "",
107 aa->aa_conn_limit, aa->aa_conn_limit_dvr, rec_count, net_count);
108 ret = -EOVERFLOW;
109 goto _return;
110 }
111 }
112
113 if(stat(de->de_config->dvr_storage, &st) || !S_ISDIR(st.st_mode)) {
114 tvherror(LS_DVR, "the directory '%s' is not accessible", de->de_config->dvr_storage);
115 ret = -EIO;
116 goto _return;
117 }
118
119 pro = de->de_config->dvr_profile;
120 prch = malloc(sizeof(*prch));
121 profile_chain_init(prch, pro, de->de_channel, 1);
122 if (profile_chain_open(prch, &de->de_config->dvr_muxcnf, 0, 0)) {
123 profile_chain_close(prch);
124 tvherror(LS_DVR, "unable to create new channel streaming chain '%s' for '%s', using default",
125 profile_get_name(pro), channel_get_name(de->de_channel));
126 pro = profile_find_by_name(NULL, NULL);
127 profile_chain_init(prch, pro, de->de_channel, 1);
128 if (profile_chain_open(prch, &de->de_config->dvr_muxcnf, 0, 0)) {
129 tvherror(LS_DVR, "unable to create channel streaming default chain '%s' for '%s'",
130 profile_get_name(pro), channel_get_name(de->de_channel));
131 profile_chain_close(prch);
132 ret = -EINVAL;
133 goto _return;
134 }
135 }
136
137 de->de_s = subscription_create_from_channel(prch, NULL, weight,
138 buf, prch->prch_flags,
139 NULL, NULL, NULL, NULL);
140 if (de->de_s == NULL) {
141 tvherror(LS_DVR, "unable to create new channel subcription for '%s' profile '%s'",
142 channel_get_name(de->de_channel), profile_get_name(pro));
143 ret = -EINVAL;
144 goto _return;
145 }
146
147 de->de_chain = prch;
148
149 atomic_set(&de->de_thread_shutdown, 0);
150 tvhthread_create(&de->de_thread, NULL, dvr_thread, de, "dvr");
151
152 if (de->de_config->dvr_preproc)
153 dvr_spawn_cmd(de, de->de_config->dvr_preproc, NULL, 1);
154 access_destroy(aa);
155 return ret;
156
157 _return:
158 profile_chain_close(prch);
159 free(prch);
160 access_destroy(aa);
161 return ret;
162 }
163
164 /**
165 *
166 */
167 void
dvr_rec_unsubscribe(dvr_entry_t * de)168 dvr_rec_unsubscribe(dvr_entry_t *de)
169 {
170 profile_chain_t *prch = de->de_chain;
171 char *postproc = NULL;
172
173 assert(de->de_s != NULL);
174 assert(prch != NULL);
175
176 de->de_in_unsubscribe = 1;
177
178 streaming_target_deliver(prch->prch_st, streaming_msg_create(SMT_EXIT));
179
180 atomic_add(&de->de_thread_shutdown, 1);
181
182 pthread_join(de->de_thread, (void **)&postproc);
183
184 if (prch->prch_muxer)
185 dvr_thread_epilog(de, postproc);
186
187 free(postproc);
188
189 subscription_unsubscribe(de->de_s, UNSUBSCRIBE_FINAL);
190 de->de_s = NULL;
191
192 de->de_chain = NULL;
193 profile_chain_close(prch);
194 free(prch);
195
196 dvr_vfs_refresh_entry(de);
197
198 de->de_in_unsubscribe = 0;
199 }
200
201 /**
202 *
203 */
204 void
dvr_rec_migrate(dvr_entry_t * de_old,dvr_entry_t * de_new)205 dvr_rec_migrate(dvr_entry_t *de_old, dvr_entry_t *de_new)
206 {
207 lock_assert(&global_lock);
208
209 de_new->de_s = de_old->de_s;
210 de_new->de_chain = de_old->de_chain;
211 de_new->de_thread = de_old->de_thread;
212 de_old->de_s = NULL;
213 de_old->de_chain = NULL;
214 de_old->de_thread = 0;
215 }
216
217 /**
218 * Replace various chars with a dash
219 * - dosubs specifies if user demanded substitutions are performed
220 */
221 static char *
cleanup_filename(dvr_config_t * cfg,char * s,int dosubs)222 cleanup_filename(dvr_config_t *cfg, char *s, int dosubs)
223 {
224 int len = strlen(s);
225 char *s1, *p;
226
227 s1 = intlconv_utf8safestr(cfg->dvr_charset_id, s, (len * 2) + 1);
228 if (s1 == NULL) {
229 tvherror(LS_DVR, "Unsupported charset %s using ASCII", cfg->dvr_charset);
230 s1 = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1),
231 s, len * 2);
232 if (s1 == NULL)
233 return NULL;
234 }
235
236 /* Do not create hidden files */
237 if (s1[0] == '.')
238 s1[0] = '_';
239 if (s1[0] == '\\' && s1[1] == '.')
240 s1[1] = '_';
241
242 for (s = s1 ; *s; s++) {
243
244 if (*s == '\\') {
245 s++;
246 if (*s == '\0')
247 break;
248 }
249
250 if (*s == '/')
251 *s = '-';
252
253 else if (cfg->dvr_whitespace_in_title &&
254 (*s == ' ' || *s == '\t') &&
255 dosubs)
256 *s = '-';
257
258 else if (cfg->dvr_clean_title &&
259 ((*s < 32) || (*s > 122) ||
260 (strchr("/:\\<>|*?\"", *s) != NULL)) &&
261 dosubs)
262 *s = '_';
263
264 else if (cfg->dvr_windows_compatible_filenames &&
265 (strchr("/:\\<>|*?\"", *s) != NULL) &&
266 dosubs)
267 *s = '_';
268 }
269
270 if (cfg->dvr_windows_compatible_filenames) {
271 /* trim trailing spaces and dots */
272 for (s = p = s1; *s; s++) {
273 if (*s == '\\')
274 s++;
275 if (*s != ' ' && *s != '.')
276 p = s + 1;
277 }
278 *p = '\0';
279 }
280
281 return s1;
282 }
283
284 /**
285 *
286 */
287 static char *
dvr_clean_directory_separator(char * s,char * tmp,size_t tmplen)288 dvr_clean_directory_separator(char *s, char *tmp, size_t tmplen)
289 {
290 char *p, *end;
291
292 if (s != tmp) {
293 end = tmp + tmplen - 1;
294 /* replace directory separator */
295 for (p = tmp; *s && p != end; s++, p++)
296 if (*s == '/')
297 *p = '-';
298 else if (*s == '"')
299 *p = '\'';
300 else
301 *p = *s;
302 *p = '\0';
303 return tmp;
304 } else {
305 for (; *s; s++)
306 if (*s == '/')
307 *s = '-';
308 else if (*s == '"')
309 *s = '\'';
310 return tmp;
311 }
312 }
313
314 static const char *
dvr_do_prefix(const char * id,const char * fmt,const char * s,char * tmp,size_t tmplen)315 dvr_do_prefix(const char *id, const char *fmt, const char *s, char *tmp, size_t tmplen)
316 {
317 if (id[0] == '?') {
318 id++;
319 if (fmt && *fmt >= '0' && *fmt <= '9') {
320 long l = strtol(fmt, NULL, 10);
321 if (l && tmplen > l)
322 tmplen = l;
323 }
324 }
325 if (s == NULL) {
326 tmp[0] = '\0';
327 } else if (s[0] && !isalpha(id[0])) {
328 snprintf(tmp, tmplen, "%c%s", id[0], s);
329 } else {
330 strlcpy(tmp, s, tmplen);
331 }
332 return dvr_clean_directory_separator(tmp, tmp, tmplen);
333 }
334
335
336 static const char *
dvr_sub_title(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)337 dvr_sub_title(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
338 {
339 return dvr_do_prefix(id, fmt, lang_str_get(((dvr_entry_t *)aux)->de_title, NULL), tmp, tmplen);
340 }
341
342 static const char *
dvr_sub_subtitle(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)343 dvr_sub_subtitle(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
344 {
345 return dvr_do_prefix(id, fmt, lang_str_get(((dvr_entry_t *)aux)->de_subtitle, NULL), tmp, tmplen);
346 }
347
348 static const char *
dvr_sub_description(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)349 dvr_sub_description(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
350 {
351 return dvr_do_prefix(id, fmt, lang_str_get(((dvr_entry_t *)aux)->de_desc, NULL), tmp, tmplen);
352 }
353
354 static const char *
dvr_sub_episode(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)355 dvr_sub_episode(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
356 {
357 const dvr_entry_t *de = aux;
358 char buf[64];
359
360 if (de->de_bcast == NULL || de->de_bcast->episode == NULL)
361 return "";
362 epg_episode_number_format(de->de_bcast->episode,
363 buf, sizeof(buf),
364 NULL, "S%02d", NULL, "E%02d", NULL);
365 return dvr_do_prefix(id, fmt, buf, tmp, tmplen);
366 }
367
368 static const char *
dvr_sub_channel(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)369 dvr_sub_channel(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
370 {
371 return dvr_do_prefix(id, fmt, DVR_CH_NAME((dvr_entry_t *)aux), tmp, tmplen);
372 }
373
374 static const char *
dvr_sub_genre(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)375 dvr_sub_genre(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
376 {
377 const dvr_entry_t *de = aux;
378 epg_genre_t *genre;
379 char buf[64];
380
381 if (de->de_bcast == NULL || de->de_bcast->episode == NULL)
382 return "";
383 genre = LIST_FIRST(&de->de_bcast->episode->genre);
384 if (!genre || !genre->code)
385 return "";
386 epg_genre_get_str(genre, 0, 1, buf, sizeof(buf), "en");
387 return dvr_do_prefix(id, fmt, buf, tmp, tmplen);
388 }
389
390 static const char *
dvr_sub_owner(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)391 dvr_sub_owner(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
392 {
393 return dvr_do_prefix(id, fmt, ((dvr_entry_t *)aux)->de_owner, tmp, tmplen);
394 }
395
396 static const char *
dvr_sub_creator(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)397 dvr_sub_creator(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
398 {
399 return dvr_do_prefix(id, fmt, ((dvr_entry_t *)aux)->de_creator, tmp, tmplen);
400 }
401
402 static const char *
dvr_sub_last_error(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)403 dvr_sub_last_error(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
404 {
405 return dvr_do_prefix(id, fmt, streaming_code2txt(((dvr_entry_t *)aux)->de_last_error), tmp, tmplen);
406 }
407
408 static const char *
dvr_sub_start(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)409 dvr_sub_start(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
410 {
411 char buf[16];
412 snprintf(buf, sizeof(buf), "%"PRItime_t, (time_t)dvr_entry_get_start_time((dvr_entry_t *)aux, 0));
413 return dvr_do_prefix(id, fmt, buf, tmp, tmplen);
414 }
415
416 static const char *
dvr_sub_errors(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)417 dvr_sub_errors(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
418 {
419 char buf[16];
420 snprintf(buf, sizeof(buf), "%"PRIu32, (uint32_t)((dvr_entry_t *)aux)->de_errors);
421 return dvr_do_prefix(id, fmt, buf, tmp, tmplen);
422 }
423
424 static const char *
dvr_sub_data_errors(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)425 dvr_sub_data_errors(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
426 {
427 char buf[16];
428 snprintf(buf, sizeof(buf), "%"PRIu32, (uint32_t)((dvr_entry_t *)aux)->de_data_errors);
429 return dvr_do_prefix(id, fmt, buf, tmp, tmplen);
430 }
431
432 static const char *
dvr_sub_stop(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)433 dvr_sub_stop(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
434 {
435 char buf[16];
436 snprintf(buf, sizeof(buf), "%"PRItime_t, (time_t)dvr_entry_get_stop_time((dvr_entry_t *)aux));
437 return dvr_do_prefix(id, fmt, buf, tmp, tmplen);
438 }
439
440 static const char *
dvr_sub_comment(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)441 dvr_sub_comment(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
442 {
443 return dvr_do_prefix(id, fmt, ((dvr_entry_t *)aux)->de_comment, tmp, tmplen);
444 }
445
446 static htsstr_substitute_t dvr_subs_entry[] = {
447 { .id = "?t", .getval = dvr_sub_title },
448 { .id = "? t", .getval = dvr_sub_title },
449 { .id = "?-t", .getval = dvr_sub_title },
450 { .id = "?_t", .getval = dvr_sub_title },
451 { .id = "?.t", .getval = dvr_sub_title },
452 { .id = "?,t", .getval = dvr_sub_title },
453 { .id = "?;t", .getval = dvr_sub_title },
454 { .id = "?s", .getval = dvr_sub_subtitle },
455 { .id = "? s", .getval = dvr_sub_subtitle },
456 { .id = "?-s", .getval = dvr_sub_subtitle },
457 { .id = "?_s", .getval = dvr_sub_subtitle },
458 { .id = "?.s", .getval = dvr_sub_subtitle },
459 { .id = "?,s", .getval = dvr_sub_subtitle },
460 { .id = "?;s", .getval = dvr_sub_subtitle },
461 { .id = "e", .getval = dvr_sub_episode },
462 { .id = " e", .getval = dvr_sub_episode },
463 { .id = "-e", .getval = dvr_sub_episode },
464 { .id = "_e", .getval = dvr_sub_episode },
465 { .id = ".e", .getval = dvr_sub_episode },
466 { .id = ",e", .getval = dvr_sub_episode },
467 { .id = ";e", .getval = dvr_sub_episode },
468 { .id = "c", .getval = dvr_sub_channel },
469 { .id = " c", .getval = dvr_sub_channel },
470 { .id = "-c", .getval = dvr_sub_channel },
471 { .id = "_c", .getval = dvr_sub_channel },
472 { .id = ".c", .getval = dvr_sub_channel },
473 { .id = ",c", .getval = dvr_sub_channel },
474 { .id = ";c", .getval = dvr_sub_channel },
475 { .id = "g", .getval = dvr_sub_genre },
476 { .id = " g", .getval = dvr_sub_genre },
477 { .id = "-g", .getval = dvr_sub_genre },
478 { .id = "_g", .getval = dvr_sub_genre },
479 { .id = ".g", .getval = dvr_sub_genre },
480 { .id = ",g", .getval = dvr_sub_genre },
481 { .id = ";g", .getval = dvr_sub_genre },
482 { .id = NULL, .getval = NULL }
483 };
484
485 static const char *
dvr_sub_strftime(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)486 dvr_sub_strftime(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
487 {
488 char fid[8], *p;
489 snprintf(fid, sizeof(fid), "%%%s", id);
490 strftime(tmp, tmplen, fid, (struct tm *)aux);
491 for (p = tmp; *p; p++)
492 if (*p == ':')
493 *p = '-';
494 return dvr_clean_directory_separator(tmp, tmp, tmplen);
495 }
496
497 static htsstr_substitute_t dvr_subs_time[] = {
498 { .id = "a", .getval = dvr_sub_strftime }, /* The abbreviated name of the day of the week */
499 { .id = "A", .getval = dvr_sub_strftime }, /* The full name of the day of the week */
500 { .id = "b", .getval = dvr_sub_strftime }, /* The abbreviated month name */
501 { .id = "B", .getval = dvr_sub_strftime }, /* The full month name */
502 { .id = "c", .getval = dvr_sub_strftime }, /* The preferred date and time representation */
503 { .id = "C", .getval = dvr_sub_strftime }, /* The century number (year/100) as a 2-digit integer */
504 { .id = "d", .getval = dvr_sub_strftime }, /* The day of the month as a decimal number (range 01 to 31) */
505 { .id = "D", .getval = dvr_sub_strftime }, /* Equivalent to %m/%d/%y */
506 { .id = "e", .getval = dvr_sub_strftime }, /* The day of the month as a decimal number (range 01 to 31) */
507
508 { .id = "Ec", .getval = dvr_sub_strftime }, /* alternatives */
509 { .id = "EC", .getval = dvr_sub_strftime },
510 { .id = "Ex", .getval = dvr_sub_strftime },
511 { .id = "EX", .getval = dvr_sub_strftime },
512 { .id = "Ey", .getval = dvr_sub_strftime },
513 { .id = "EY", .getval = dvr_sub_strftime },
514
515 { .id = "F", .getval = dvr_sub_strftime }, /* Equivalent to %m/%d/%y */
516 { .id = "G", .getval = dvr_sub_strftime }, /* The ISO 8601 week-based year with century */
517 { .id = "g", .getval = dvr_sub_strftime }, /* Like %G, but without century */
518 { .id = "h", .getval = dvr_sub_strftime }, /* Equivalent to %b */
519 { .id = "H", .getval = dvr_sub_strftime }, /* The hour (range 00 to 23) */
520 { .id = "j", .getval = dvr_sub_strftime }, /* The day of the year (range 000 to 366) */
521 { .id = "k", .getval = dvr_sub_strftime }, /* The hour (range 0 to 23) - with space */
522 { .id = "l", .getval = dvr_sub_strftime }, /* The hour (range 1 to 12) - with space */
523 { .id = "m", .getval = dvr_sub_strftime }, /* The month (range 01 to 12) */
524 { .id = "M", .getval = dvr_sub_strftime }, /* The minute (range 00 to 59) */
525
526 { .id = "Od", .getval = dvr_sub_strftime }, /* alternatives */
527 { .id = "Oe", .getval = dvr_sub_strftime },
528 { .id = "OH", .getval = dvr_sub_strftime },
529 { .id = "OI", .getval = dvr_sub_strftime },
530 { .id = "Om", .getval = dvr_sub_strftime },
531 { .id = "OM", .getval = dvr_sub_strftime },
532 { .id = "OS", .getval = dvr_sub_strftime },
533 { .id = "Ou", .getval = dvr_sub_strftime },
534 { .id = "OU", .getval = dvr_sub_strftime },
535 { .id = "OV", .getval = dvr_sub_strftime },
536 { .id = "Ow", .getval = dvr_sub_strftime },
537 { .id = "OW", .getval = dvr_sub_strftime },
538 { .id = "Oy", .getval = dvr_sub_strftime },
539
540 { .id = "p", .getval = dvr_sub_strftime }, /* AM/PM */
541 { .id = "P", .getval = dvr_sub_strftime }, /* am/pm */
542 { .id = "r", .getval = dvr_sub_strftime }, /* a.m./p.m. */
543 { .id = "R", .getval = dvr_sub_strftime }, /* %H:%M */
544 { .id = "s", .getval = dvr_sub_strftime }, /* The number of seconds since the Epoch */
545 { .id = "S", .getval = dvr_sub_strftime }, /* The seconds (range 00 to 60) */
546 { .id = "T", .getval = dvr_sub_strftime }, /* %H:%M:%S */
547 { .id = "u", .getval = dvr_sub_strftime }, /* The day of the week as a decimal, range 1 to 7, Monday being 1 */
548 { .id = "U", .getval = dvr_sub_strftime }, /* The week number of the current year as a decimal number, range 00 to 53 (Sunday) */
549 { .id = "V", .getval = dvr_sub_strftime }, /* The ISO 8601 week number (range 01 to 53) */
550 { .id = "w", .getval = dvr_sub_strftime }, /* The day of the week as a decimal, range 0 to 6, Sunday being 0 */
551 { .id = "W", .getval = dvr_sub_strftime }, /* The week number of the current year as a decimal number, range 00 to 53 (Monday) */
552 { .id = "x", .getval = dvr_sub_strftime }, /* The preferred date representation */
553 { .id = "X", .getval = dvr_sub_strftime }, /* The preferred time representation */
554 { .id = "y", .getval = dvr_sub_strftime }, /* The year as a decimal number without a century (range 00 to 99) */
555 { .id = "Y", .getval = dvr_sub_strftime }, /* The year as a decimal number including the century */
556 { .id = "z", .getval = dvr_sub_strftime }, /* The +hhmm or -hhmm numeric timezone */
557 { .id = "Z", .getval = dvr_sub_strftime }, /* The timezone name or abbreviation */
558
559 { .id = NULL, .getval = NULL }
560 };
561
562 static const char *
dvr_sub_str(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)563 dvr_sub_str(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
564 {
565 return (const char *)aux;
566 }
567
568 static const char *
dvr_sub_str_separator(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)569 dvr_sub_str_separator(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
570 {
571 strlcpy(tmp, (const char *)aux, tmplen);
572 return dvr_clean_directory_separator(tmp, tmp, tmplen);
573 }
574
575 static htsstr_substitute_t dvr_subs_extension[] = {
576 { .id = "x", .getval = dvr_sub_str_separator },
577 { .id = NULL, .getval = NULL }
578 };
579
580 static htsstr_substitute_t dvr_subs_tally[] = {
581 { .id = "n", .getval = dvr_sub_str_separator },
582 { .id = NULL, .getval = NULL }
583 };
584
585 static htsstr_substitute_t dvr_subs_postproc_entry[] = {
586 { .id = "t", .getval = dvr_sub_title },
587 { .id = "s", .getval = dvr_sub_subtitle },
588 { .id = "p", .getval = dvr_sub_episode },
589 { .id = "d", .getval = dvr_sub_description },
590 { .id = "g", .getval = dvr_sub_genre },
591 { .id = "c", .getval = dvr_sub_channel },
592 { .id = "e", .getval = dvr_sub_last_error },
593 { .id = "C", .getval = dvr_sub_creator },
594 { .id = "O", .getval = dvr_sub_owner },
595 { .id = "S", .getval = dvr_sub_start },
596 { .id = "E", .getval = dvr_sub_stop },
597 { .id = "r", .getval = dvr_sub_errors },
598 { .id = "R", .getval = dvr_sub_data_errors },
599 { .id = "Z", .getval = dvr_sub_comment },
600 { .id = NULL, .getval = NULL }
601 };
602
603 static const char *
dvr_sub_basename(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)604 dvr_sub_basename(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
605 {
606 strlcpy(tmp, (const char *)aux, tmplen);
607 return basename(tmp);
608 }
609
610 static htsstr_substitute_t dvr_subs_postproc_filename[] = {
611 { .id = "f", .getval = dvr_sub_str },
612 { .id = "b", .getval = dvr_sub_basename },
613 { .id = NULL, .getval = NULL }
614 };
615
616 static const char *
dvr_sub_basic_info(const char * id,const char * fmt,const void * aux,char * tmp,size_t tmplen)617 dvr_sub_basic_info(const char *id, const char *fmt, const void *aux, char *tmp, size_t tmplen)
618 {
619 htsmsg_t *info = (htsmsg_t *)aux, *e;
620 htsmsg_field_t *f;
621 const char *s;
622 size_t l = 0;
623
624 if (info)
625 info = htsmsg_get_list(info, "info");
626 if (!info)
627 return "";
628
629 tmp[0] = '\0';
630 HTSMSG_FOREACH(f, info) {
631 if (!(e = htsmsg_field_get_map(f))) continue;
632 if ((s = htsmsg_get_str(e, "type")) != NULL)
633 tvh_strlcatf(tmp, tmplen, l, "%s%s", l > 0 ? "," : "", s);
634 }
635 return tmp;
636 }
637
638 static htsstr_substitute_t dvr_subs_postproc_info[] = {
639 { .id = "i", .getval = dvr_sub_basic_info },
640 { .id = NULL, .getval = NULL }
641 };
642
dvr_find_last_path_component(char * path)643 static char *dvr_find_last_path_component(char *path)
644 {
645 char *res, *p;
646 for (p = path, res = NULL; *p; p++) {
647 if (*p == '\\') {
648 p++;
649 } else {
650 if (*p == '/')
651 res = p;
652 }
653 }
654 return res;
655 }
656
dvr_find_next_path_component(char * path)657 static char *dvr_find_next_path_component(char *path)
658 {
659 char *res, *p;
660 for (p = res = path; *p; p++) {
661 if (*p == '\\') {
662 p++;
663 } else {
664 if (*p == '/')
665 return p + 1;
666 }
667 }
668 return NULL;
669 }
670
671 /**
672 * Filename generator
673 *
674 * - convert from utf8
675 * - avoid duplicate filenames
676 *
677 */
678 static int
pvr_generate_filename(dvr_entry_t * de,const streaming_start_t * ss)679 pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
680 {
681 char filename[PATH_MAX];
682 char path[PATH_MAX + 1];
683 char ptmp[PATH_MAX];
684 char number[16];
685 char tmp[MAX(PATH_MAX, 512)];
686 char *lastpath = NULL;
687 int tally = 0;
688 struct stat st;
689 char *s, *x, *fmtstr, *dirsep;
690 struct tm tm;
691 dvr_config_t *cfg;
692 htsmsg_t *m;
693 size_t l, j, k;
694 long max;
695 int dir_dosubs;
696
697 if (de == NULL)
698 return -1;
699
700 cfg = de->de_config;
701 if (tvh_str_default(cfg->dvr_storage, NULL) == NULL)
702 return -1;
703
704 dir_dosubs = de->de_directory == NULL ||
705 (de->de_directory[0] == '$' &&
706 de->de_directory[1] == '$');
707
708 localtime_r(&de->de_start, &tm);
709
710 strlcpy(path, cfg->dvr_storage, sizeof(path));
711 l = strlen(path);
712 if (l + 1 >= sizeof(path)) {
713 tvherror(LS_DVR, "wrong storage path");
714 return -1;
715 }
716
717 /* Remove trailing slash */
718 while (l > 0 && path[l-1] == '/')
719 path[--l] = '\0';
720 if (l + 1 >= sizeof(path))
721 l--;
722 path[l++] = '/';
723 path[l] = '\0';
724
725 fmtstr = cfg->dvr_pathname;
726 while (*fmtstr == '/')
727 fmtstr++;
728
729 /* Substitute DVR entry formatters */
730 htsstr_substitute(fmtstr, path + l, sizeof(path) - l, '$', dvr_subs_entry, de, tmp, sizeof(tmp));
731
732 /* Own directory? */
733 if (de->de_directory) {
734 dirsep = dvr_find_last_path_component(path + l);
735 if (dirsep)
736 strcpy(filename, dirsep + 1);
737 else
738 strcpy(filename, path + l);
739 if (dir_dosubs) {
740 htsstr_substitute(de->de_directory+2, ptmp, sizeof(ptmp), '$', dvr_subs_entry, de, tmp, sizeof(tmp));
741 } else {
742 strlcpy(ptmp, de->de_directory, sizeof(ptmp));
743 }
744 s = ptmp;
745 while (*s == '/')
746 s++;
747 j = strlen(s);
748 while (j > 0 && s[j-1] == '/')
749 j--;
750 s[j] = '\0';
751 snprintf(path + l, sizeof(path) - l, "%s", s);
752 snprintf(path + l + j, sizeof(path) - l + j, "/%s", filename);
753 }
754
755 /* Substitute time formatters */
756 htsstr_substitute(path + l, filename, sizeof(filename), '%', dvr_subs_time, &tm, tmp, sizeof(tmp));
757
758 /* Substitute extension */
759 htsstr_substitute(filename, path + l, sizeof(path) - l, '$', dvr_subs_extension,
760 muxer_suffix(de->de_chain->prch_muxer, ss) ?: "", tmp, sizeof(tmp));
761
762 /* Cleanup all directory names */
763 x = path + l;
764 filename[j = 0] = '\0';
765 while (1) {
766 dirsep = dvr_find_next_path_component(x);
767 if (tvh_str_default(dirsep, NULL) == NULL)
768 break;
769 *(dirsep - 1) = '\0';
770 if (*x) {
771 s = cleanup_filename(cfg, x, dir_dosubs);
772 tvh_strlcatf(filename, sizeof(filename), j, "%s/", s);
773 free(s);
774 }
775 x = dirsep;
776 }
777 tvh_strlcatf(filename, sizeof(filename), j, "%s", x);
778 snprintf(path + l, sizeof(path) - l, "%s", filename);
779
780 /* Deescape directory path and create directory tree */
781 dirsep = dvr_find_last_path_component(path + l);
782 if (dirsep) {
783 *dirsep = '\0';
784 dirsep++;
785 } else {
786 if (l > 0) {
787 assert(path[l-1] == '/');
788 path[l-1] = '\0';
789 }
790 dirsep = path + l;
791 }
792 htsstr_unescape_to(path, filename, sizeof(filename));
793 if (makedirs(LS_DVR, filename,
794 cfg->dvr_muxcnf.m_directory_permissions, 0, -1, -1) != 0)
795 return -1;
796 max = pathconf(filename, _PC_NAME_MAX);
797 if (max < 8)
798 max = NAME_MAX;
799 if (max > 255 && cfg->dvr_windows_compatible_filenames)
800 max = 255;
801 max -= 2;
802 j = strlen(filename);
803 snprintf(filename + j, sizeof(filename) - j, "/%s", dirsep);
804 if (filename[j] == '/')
805 j++;
806
807 /* Unique filename loop */
808 while (1) {
809
810 /* Prepare the name portion */
811 if (tally > 0) {
812 snprintf(number, sizeof(number), "-%d", tally);
813 } else {
814 number[0] = '\0';
815 }
816 /* Check the maximum filename length */
817 l = strlen(number);
818 k = strlen(filename + j);
819 if (l + k > max) {
820 s = (char *)htsstr_substitute_find(filename + j, '$');
821 if (s == NULL || s - (filename + j) < (l + k) - max) {
822 cut1:
823 l = j + (max - l);
824 if (filename[l - 1] == '$') /* not optimal */
825 filename[l + 1] = '\0';
826 else
827 filename[l] = '\0';
828 } else {
829 x = (char *)htsstr_escape_find(filename + j, s - (filename + j) - ((l + k) - max));
830 if (x == NULL)
831 goto cut1;
832 k = strlen(s);
833 memmove(x, s, k);
834 x[k] = '\0';
835 }
836 }
837
838 htsstr_substitute(filename + j, ptmp, sizeof(ptmp), '$', dvr_subs_tally, number, tmp, sizeof(tmp));
839 s = cleanup_filename(cfg, ptmp, 1);
840 if (s == NULL) {
841 free(lastpath);
842 return -1;
843 }
844
845 /* Construct the final filename */
846 memcpy(path, filename, j);
847 path[j] = '\0';
848 htsstr_unescape_to(s, path + j, sizeof(path) - j);
849 free(s);
850
851 if (lastpath) {
852 if (strcmp(path, lastpath) == 0) {
853 free(lastpath);
854 tvherror(LS_DVR, "unable to create unique name (missing $n in format string?)");
855 return -1;
856 }
857 }
858
859 if (stat(path, &st) == -1) {
860 tvhdebug(LS_DVR, "File \"%s\" -- %s -- Using for recording",
861 path, strerror(errno));
862 break;
863 }
864
865 tvhdebug(LS_DVR, "Overwrite protection, file \"%s\" exists", path);
866
867 free(lastpath);
868 lastpath = strdup(path);
869 tally++;
870 }
871
872 free(lastpath);
873 if (de->de_files == NULL)
874 de->de_files = htsmsg_create_list();
875 m = htsmsg_create_map();
876 htsmsg_add_str(m, "filename", path);
877 htsmsg_add_msg(de->de_files, NULL, m);
878
879 return 0;
880 }
881
882 /**
883 *
884 */
885 static void
dvr_rec_fatal_error(dvr_entry_t * de,const char * fmt,...)886 dvr_rec_fatal_error(dvr_entry_t *de, const char *fmt, ...)
887 {
888 char msgbuf[256];
889
890 va_list ap;
891 va_start(ap, fmt);
892
893 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
894 va_end(ap);
895
896 tvherror(LS_DVR, "Recording error: \"%s\": %s",
897 dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL), msgbuf);
898 }
899
900 /**
901 *
902 */
903 static void
dvr_notify(dvr_entry_t * de)904 dvr_notify(dvr_entry_t *de)
905 {
906 if (de->de_last_notify + sec2mono(5) < mclk()) {
907 idnode_notify_changed(&de->de_id);
908 de->de_last_notify = mclk();
909 htsp_dvr_entry_update_stats(de);
910 }
911 }
912
913 /**
914 *
915 */
916 static void
dvr_rec_set_state(dvr_entry_t * de,dvr_rs_state_t newstate,int error)917 dvr_rec_set_state(dvr_entry_t *de, dvr_rs_state_t newstate, int error)
918 {
919 if (de->de_last_error != error && error)
920 de->de_errors++;
921 dvr_entry_set_state(de, de->de_sched_state, newstate, error);
922 }
923
924 /**
925 *
926 */
927 static int
dvr_rec_start(dvr_entry_t * de,const streaming_start_t * ss)928 dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
929 {
930 const source_info_t *si = &ss->ss_si;
931 streaming_start_t *ss_copy;
932 const streaming_start_component_t *ssc;
933 char res[14], asp[14], sr[6], ch[7];
934 dvr_config_t *cfg = de->de_config;
935 profile_chain_t *prch = de->de_chain;
936 htsmsg_t *info, *e;
937 htsmsg_field_t *f;
938 muxer_t *muxer;
939 struct stat st;
940 int i;
941
942 if (!cfg) {
943 dvr_rec_fatal_error(de, "Unable to determine config profile");
944 return -1;
945 }
946
947 if (!prch) {
948 dvr_rec_fatal_error(de, "Unable to determine stream profile");
949 return -1;
950 }
951
952 if (stat(cfg->dvr_storage, &st) || !S_ISDIR(st.st_mode)) {
953 dvr_rec_fatal_error(de, "Unable to create file in directory '%s", cfg->dvr_storage);
954 return -1;
955 }
956
957 if (!dvr_vfs_rec_start_check(cfg)) {
958 dvr_rec_fatal_error(de, "Not enough free disk space");
959 return SM_CODE_NO_SPACE;
960 }
961
962 if (!(muxer = prch->prch_muxer)) {
963 if (profile_chain_reopen(prch, &cfg->dvr_muxcnf, 0)) {
964 dvr_rec_fatal_error(de, "Unable to reopen muxer");
965 return -1;
966 }
967 muxer = prch->prch_muxer;
968 }
969
970 if(!muxer) {
971 dvr_rec_fatal_error(de, "Unable to create muxer");
972 return -1;
973 }
974
975 if(pvr_generate_filename(de, ss) != 0) {
976 dvr_rec_fatal_error(de, "Unable to create file");
977 return -1;
978 }
979
980 if(muxer_open_file(muxer, dvr_get_filename(de))) {
981 dvr_rec_fatal_error(de, "Unable to open file");
982 return -1;
983 }
984
985 dvr_vfs_refresh_entry(de);
986
987 ss_copy = streaming_start_copy(ss);
988
989 if(muxer_init(muxer, ss_copy, lang_str_get(de->de_title, NULL))) {
990 dvr_rec_fatal_error(de, "Unable to init file");
991 goto _err;
992 }
993
994 if(cfg->dvr_tag_files) {
995 if(muxer_write_meta(muxer, de->de_bcast, de->de_comment)) {
996 dvr_rec_fatal_error(de, "Unable to write meta data");
997 goto _err;
998 }
999 }
1000
1001 tvhinfo(LS_DVR, "%s from "
1002 "adapter: \"%s\", "
1003 "network: \"%s\", mux: \"%s\", provider: \"%s\", "
1004 "service: \"%s\"",
1005
1006 dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL),
1007 si->si_adapter ?: "<N/A>",
1008 si->si_network ?: "<N/A>",
1009 si->si_mux ?: "<N/A>",
1010 si->si_provider ?: "<N/A>",
1011 si->si_service ?: "<N/A>");
1012
1013
1014 tvhinfo(LS_DVR,
1015 " # %-16s %-4s %-10s %-12s %-11s %-8s",
1016 "type",
1017 "lang",
1018 "resolution",
1019 "aspect ratio",
1020 "sample rate",
1021 "channels");
1022
1023 info = htsmsg_create_list();
1024 for(i = 0; i < ss_copy->ss_num_components; i++) {
1025 ssc = &ss_copy->ss_components[i];
1026
1027 if(ssc->ssc_muxer_disabled)
1028 continue;
1029
1030 e = htsmsg_create_map();
1031 htsmsg_add_str(e, "type", streaming_component_type2txt(ssc->ssc_type));
1032 if (ssc->ssc_lang[0])
1033 htsmsg_add_str(e, "language", ssc->ssc_lang);
1034
1035 if(SCT_ISAUDIO(ssc->ssc_type)) {
1036 htsmsg_add_u32(e, "audio_type", ssc->ssc_audio_type);
1037 if(ssc->ssc_audio_version)
1038 htsmsg_add_u32(e, "audio_version", ssc->ssc_audio_version);
1039 if(ssc->ssc_sri)
1040 snprintf(sr, sizeof(sr), "%d", sri_to_rate(ssc->ssc_sri));
1041 else
1042 strcpy(sr, "?");
1043
1044 if(ssc->ssc_channels == 6)
1045 snprintf(ch, sizeof(ch), "5.1");
1046 else if(ssc->ssc_channels == 0)
1047 strcpy(ch, "?");
1048 else
1049 snprintf(ch, sizeof(ch), "%d", ssc->ssc_channels);
1050 } else {
1051 sr[0] = ch[0] = 0;
1052 }
1053
1054 if(SCT_ISVIDEO(ssc->ssc_type)) {
1055 if(ssc->ssc_width && ssc->ssc_height)
1056 snprintf(res, sizeof(res), "%dx%d",
1057 ssc->ssc_width, ssc->ssc_height);
1058 else
1059 strcpy(res, "?");
1060
1061 if(ssc->ssc_aspect_num && ssc->ssc_aspect_den)
1062 snprintf(asp, sizeof(asp), "%d:%d",
1063 ssc->ssc_aspect_num, ssc->ssc_aspect_den);
1064 else
1065 strcpy(asp, "?");
1066
1067 htsmsg_add_u32(e, "width", ssc->ssc_width);
1068 htsmsg_add_u32(e, "height", ssc->ssc_height);
1069 htsmsg_add_u32(e, "duration", ssc->ssc_frameduration);
1070 htsmsg_add_u32(e, "aspect_num", ssc->ssc_aspect_num);
1071 htsmsg_add_u32(e, "aspect_den", ssc->ssc_aspect_den);
1072 } else {
1073 res[0] = asp[0] = 0;
1074 }
1075
1076 if (SCT_ISSUBTITLE(ssc->ssc_type)) {
1077 htsmsg_add_u32(e, "composition_id", ssc->ssc_composition_id);
1078 htsmsg_add_u32(e, "ancillary_id", ssc->ssc_ancillary_id);
1079 }
1080
1081 tvhinfo(LS_DVR,
1082 "%2d %-16s %-4s %-10s %-12s %-11s %-8s %s",
1083 ssc->ssc_index,
1084 streaming_component_type2txt(ssc->ssc_type),
1085 ssc->ssc_lang,
1086 res,
1087 asp,
1088 sr,
1089 ch,
1090 ssc->ssc_disabled ? "<disabled, no valid input>" : "");
1091 htsmsg_add_msg(info, NULL, e);
1092 }
1093
1094 streaming_start_unref(ss_copy);
1095
1096 /* update the info field for a filename */
1097 if ((f = htsmsg_field_last(de->de_files)) != NULL &&
1098 (e = htsmsg_field_get_map(f)) != NULL) {
1099 htsmsg_set_msg(e, "info", info);
1100 htsmsg_set_s64(e, "start", gclk());
1101 } else {
1102 htsmsg_destroy(info);
1103 }
1104 return 0;
1105
1106 _err:
1107 streaming_start_unref(ss_copy);
1108 return -1;
1109 }
1110
1111 /**
1112 *
1113 */
1114 static inline int
dvr_thread_global_lock(dvr_entry_t * de,int * run)1115 dvr_thread_global_lock(dvr_entry_t *de, int *run)
1116 {
1117 if (atomic_add(&de->de_thread_shutdown, 1) > 0) {
1118 *run = 0;
1119 return 0;
1120 }
1121 pthread_mutex_lock(&global_lock);
1122 return 1;
1123 }
1124
1125 /**
1126 *
1127 */
1128 static inline void
dvr_thread_global_unlock(dvr_entry_t * de)1129 dvr_thread_global_unlock(dvr_entry_t *de)
1130 {
1131 pthread_mutex_unlock(&global_lock);
1132 atomic_dec(&de->de_thread_shutdown, 1);
1133 }
1134
1135 /**
1136 *
1137 */
1138 static void
dvr_streaming_restart(dvr_entry_t * de,int * run)1139 dvr_streaming_restart(dvr_entry_t *de, int *run)
1140 {
1141 if (dvr_thread_global_lock(de, run)) {
1142 service_restart(de->de_s->ths_service);
1143 dvr_thread_global_unlock(de);
1144 }
1145 }
1146
1147 /**
1148 *
1149 */
1150 static int
dvr_thread_pkt_stats(dvr_entry_t * de,th_pkt_t * pkt,int payload)1151 dvr_thread_pkt_stats(dvr_entry_t *de, th_pkt_t *pkt, int payload)
1152 {
1153 th_subscription_t *ts;
1154 int ret = 0;
1155
1156 if ((ts = de->de_s) != NULL) {
1157 if (pkt->pkt_err) {
1158 de->de_data_errors += pkt->pkt_err;
1159 ret = 1;
1160 }
1161 if (payload && pkt->pkt_payload)
1162 subscription_add_bytes_out(ts, pktbuf_len(pkt->pkt_payload));
1163 }
1164 return ret;
1165 }
1166
1167 /**
1168 *
1169 */
1170 static int
dvr_thread_mpegts_stats(dvr_entry_t * de,void * sm_data)1171 dvr_thread_mpegts_stats(dvr_entry_t *de, void *sm_data)
1172 {
1173 th_subscription_t *ts;
1174 pktbuf_t *pb = sm_data;
1175 int ret = 0;
1176
1177 if (pb) {
1178 if ((ts = de->de_s) != NULL) {
1179 if (pb->pb_err) {
1180 de->de_data_errors += pb->pb_err;
1181 ret = 1;
1182 }
1183 subscription_add_bytes_out(ts, pktbuf_len(pb));
1184 }
1185 }
1186 return ret;
1187 }
1188
1189 /**
1190 *
1191 */
1192 static int
dvr_thread_rec_start(dvr_entry_t ** _de,streaming_start_t * ss,int * run,int * started,int64_t * dts_offset,const char * postproc)1193 dvr_thread_rec_start(dvr_entry_t **_de, streaming_start_t *ss,
1194 int *run, int *started, int64_t *dts_offset,
1195 const char *postproc)
1196 {
1197 dvr_entry_t *de = *_de;
1198 profile_chain_t *prch = de->de_chain;
1199 int ret = 0;
1200
1201 if (*started) {
1202 if (muxer_reconfigure(prch->prch_muxer, ss) >= 0)
1203 return 1;
1204 tvhwarn(LS_DVR, "Unable to reconfigure \"%s\"",
1205 dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL));
1206
1207 // Try to restart the recording if the muxer doesn't
1208 // support reconfiguration of the streams.
1209 if (!dvr_thread_global_lock(de, run)) {
1210 *dts_offset = PTS_UNSET;
1211 *started = 0;
1212 return 0;
1213 }
1214 dvr_thread_epilog(de, postproc);
1215 *dts_offset = PTS_UNSET;
1216 *started = 0;
1217 if (de->de_config->dvr_clone)
1218 *_de = dvr_entry_clone(de);
1219 dvr_thread_global_unlock(de);
1220 de = *_de;
1221 }
1222
1223 if (!*started) {
1224 if (!dvr_thread_global_lock(de, run))
1225 return 0;
1226 dvr_rec_set_state(de, DVR_RS_WAIT_PROGRAM_START, 0);
1227 int code = dvr_rec_start(de, ss);
1228 if(code == 0) {
1229 ret = 1;
1230 *started = 1;
1231 } else
1232 dvr_stop_recording(de, code == SM_CODE_NO_SPACE ? SM_CODE_NO_SPACE : SM_CODE_INVALID_TARGET, 1, 0);
1233 dvr_thread_global_unlock(de);
1234 }
1235 return ret;
1236 }
1237
1238 /**
1239 *
1240 */
1241 static inline int
dts_pts_valid(th_pkt_t * pkt,int64_t dts_offset)1242 dts_pts_valid(th_pkt_t *pkt, int64_t dts_offset)
1243 {
1244 if (pkt->pkt_dts == PTS_UNSET ||
1245 pkt->pkt_pts == PTS_UNSET ||
1246 dts_offset == PTS_UNSET ||
1247 pkt->pkt_dts < dts_offset ||
1248 pkt->pkt_pts < dts_offset)
1249 return 0;
1250 return 1;
1251 }
1252
1253 /**
1254 *
1255 */
1256 static int64_t
get_dts_ref(th_pkt_t * pkt,streaming_start_t * ss)1257 get_dts_ref(th_pkt_t *pkt, streaming_start_t *ss)
1258 {
1259 const streaming_start_component_t *ssc;
1260 int64_t audio = PTS_UNSET;
1261 int i;
1262
1263 if (pkt->pkt_dts == PTS_UNSET)
1264 return PTS_UNSET;
1265 for (i = 0; i < ss->ss_num_components; i++) {
1266 ssc = &ss->ss_components[i];
1267 if (ssc->ssc_index == pkt->pkt_componentindex) {
1268 if (SCT_ISVIDEO(ssc->ssc_type))
1269 return pkt->pkt_dts;
1270 if (audio == PTS_UNSET && SCT_ISAUDIO(ssc->ssc_type))
1271 audio = pkt->pkt_dts;
1272 }
1273 }
1274 return audio;
1275 }
1276
1277 /**
1278 *
1279 */
1280 static void *
dvr_thread(void * aux)1281 dvr_thread(void *aux)
1282 {
1283 dvr_entry_t *de = aux;
1284 profile_chain_t *prch = de->de_chain;
1285 streaming_queue_t *sq = &prch->prch_sq;
1286 struct streaming_message_queue backlog;
1287 streaming_message_t *sm, *sm2;
1288 th_pkt_t *pkt, *pkt2, *pkt3;
1289 streaming_start_t *ss = NULL;
1290 int run = 1, started = 0, muxing = 0, comm_skip, rs;
1291 int epg_running = 0, old_epg_running, epg_pause = 0;
1292 int commercial = COMMERCIAL_UNKNOWN;
1293 int running_disabled;
1294 int64_t packets = 0, dts_offset = PTS_UNSET;
1295 time_t real_start, start_time = 0, running_start = 0, running_stop = 0;
1296 char *postproc;
1297 char ubuf[UUID_HEX_SIZE];
1298
1299 if (!dvr_thread_global_lock(de, &run))
1300 return NULL;
1301 comm_skip = de->de_config->dvr_skip_commercials;
1302 postproc = de->de_config->dvr_postproc ? strdup(de->de_config->dvr_postproc) : NULL;
1303 running_disabled = dvr_entry_get_epg_running(de) <= 0;
1304 real_start = dvr_entry_get_start_time(de, 0);
1305 tvhtrace(LS_DVR, "%s - recoding thread started for \"%s\"",
1306 idnode_uuid_as_str(&de->de_id, ubuf), lang_str_get(de->de_title, NULL));
1307 dvr_thread_global_unlock(de);
1308
1309 TAILQ_INIT(&backlog);
1310
1311 pthread_mutex_lock(&sq->sq_mutex);
1312 while(run) {
1313 sm = TAILQ_FIRST(&sq->sq_queue);
1314 if(sm == NULL) {
1315 tvh_cond_wait(&sq->sq_cond, &sq->sq_mutex);
1316 continue;
1317 }
1318 streaming_queue_remove(sq, sm);
1319
1320 old_epg_running = epg_running;
1321 if (running_disabled) {
1322 epg_running = real_start <= gclk();
1323 } else if (sm->sm_type == SMT_PACKET || sm->sm_type == SMT_MPEGTS) {
1324 running_start = atomic_get_time_t(&de->de_running_start);
1325 running_stop = atomic_get_time_t(&de->de_running_stop);
1326 if (running_start > 0) {
1327 epg_running = running_start >= running_stop ? 1 : 0;
1328 if (epg_running && atomic_get_time_t(&de->de_running_pause) >= running_start)
1329 epg_running = 2;
1330 } else if (running_stop == 0) {
1331 if (start_time + 2 >= gclk()) {
1332 TAILQ_INSERT_TAIL(&backlog, sm, sm_link);
1333 continue;
1334 } else {
1335 if (TAILQ_FIRST(&backlog))
1336 streaming_queue_clear(&backlog);
1337 epg_running = real_start <= gclk();
1338 }
1339 } else {
1340 epg_running = 0;
1341 }
1342 }
1343 if (epg_running != old_epg_running)
1344 tvhtrace(LS_DVR, "%s - running flag changed from %d to %d",
1345 idnode_uuid_as_str(&de->de_id, ubuf), old_epg_running, epg_running);
1346
1347 pthread_mutex_unlock(&sq->sq_mutex);
1348
1349 switch(sm->sm_type) {
1350
1351 case SMT_PACKET:
1352 pkt = sm->sm_data;
1353
1354 rs = DVR_RS_RUNNING;
1355 if (!epg_running)
1356 rs = DVR_RS_EPG_WAIT;
1357 else if (pkt->pkt_commercial == COMMERCIAL_YES || epg_running == 2)
1358 rs = DVR_RS_COMMERCIAL;
1359 dvr_rec_set_state(de, rs, 0);
1360
1361 if ((rs == DVR_RS_COMMERCIAL && comm_skip) || !epg_running) {
1362 if (ss && packets && running_start == 0) {
1363 dvr_streaming_restart(de, &run);
1364 packets = 0;
1365 started = 0;
1366 }
1367 break;
1368 }
1369
1370 if (epg_pause != (epg_running == 2)) {
1371 epg_pause = epg_running == 2;
1372 if (muxing) muxer_add_marker(prch->prch_muxer);
1373 } else if (commercial != pkt->pkt_commercial) {
1374 commercial = pkt->pkt_commercial;
1375 if (muxing) muxer_add_marker(prch->prch_muxer);
1376 } else if (atomic_exchange(&de->de_running_change, 0)) {
1377 if (muxing) muxer_add_marker(prch->prch_muxer);
1378 }
1379
1380 if (ss == NULL)
1381 break;
1382
1383 if (muxing == 0) {
1384 if (!dvr_thread_rec_start(&de, ss, &run, &started, &dts_offset, postproc))
1385 break;
1386 tvhtrace(LS_DVR, "%s - muxing activated", idnode_uuid_as_str(&de->de_id, ubuf));
1387 }
1388
1389 muxing = 1;
1390 while ((sm2 = TAILQ_FIRST(&backlog)) != NULL) {
1391 TAILQ_REMOVE(&backlog, sm2, sm_link);
1392 pkt2 = sm2->sm_data;
1393 if (pkt2->pkt_dts != PTS_UNSET) {
1394 if (dts_offset == PTS_UNSET)
1395 dts_offset = get_dts_ref(pkt2, ss);
1396 if (dts_pts_valid(pkt2, dts_offset)) {
1397 pkt3 = pkt_copy_shallow(pkt2);
1398 pkt3->pkt_dts -= dts_offset;
1399 if (pkt3->pkt_pts != PTS_UNSET)
1400 pkt3->pkt_pts -= dts_offset;
1401 dvr_thread_pkt_stats(de, pkt3, 1);
1402 muxer_write_pkt(prch->prch_muxer, sm2->sm_type, pkt3);
1403 } else {
1404 dvr_thread_pkt_stats(de, pkt2, 0);
1405 }
1406 }
1407 streaming_msg_free(sm2);
1408 }
1409 if (dts_offset == PTS_UNSET)
1410 dts_offset = get_dts_ref(pkt, ss);
1411 if (dts_pts_valid(pkt, dts_offset)) {
1412 pkt3 = pkt_copy_shallow(pkt);
1413 pkt3->pkt_dts -= dts_offset;
1414 if (pkt3->pkt_pts != PTS_UNSET)
1415 pkt3->pkt_pts -= dts_offset;
1416 dvr_thread_pkt_stats(de, pkt3, 1);
1417 muxer_write_pkt(prch->prch_muxer, sm->sm_type, pkt3);
1418 } else {
1419 dvr_thread_pkt_stats(de, pkt, 0);
1420 }
1421 dvr_notify(de);
1422 packets++;
1423 break;
1424
1425 case SMT_MPEGTS:
1426 rs = DVR_RS_RUNNING;
1427 if (!epg_running)
1428 rs = DVR_RS_EPG_WAIT;
1429 else if (epg_running == 2)
1430 rs = DVR_RS_COMMERCIAL;
1431 dvr_rec_set_state(de, rs, 0);
1432
1433 if (ss == NULL)
1434 break;
1435
1436 if ((rs == DVR_RS_COMMERCIAL && comm_skip) || !epg_running) {
1437 if (packets && running_start == 0) {
1438 dvr_streaming_restart(de, &run);
1439 packets = 0;
1440 started = 0;
1441 }
1442 break;
1443 }
1444
1445 if (epg_pause != (epg_running == 2)) {
1446 epg_pause = epg_running == 2;
1447 if (muxing) muxer_add_marker(prch->prch_muxer);
1448 } else if (atomic_exchange(&de->de_running_change, 0)) {
1449 if (muxing) muxer_add_marker(prch->prch_muxer);
1450 }
1451
1452 if (muxing == 0) {
1453 if (!dvr_thread_rec_start(&de, ss, &run, &started, &dts_offset, postproc))
1454 break;
1455 tvhtrace(LS_DVR, "%s - muxing activated", idnode_uuid_as_str(&de->de_id, ubuf));
1456 }
1457
1458 muxing = 1;
1459 while ((sm2 = TAILQ_FIRST(&backlog)) != NULL) {
1460 TAILQ_REMOVE(&backlog, sm2, sm_link);
1461 dvr_thread_mpegts_stats(de, sm2->sm_data);
1462 muxer_write_pkt(prch->prch_muxer, sm2->sm_type, sm2->sm_data);
1463 sm2->sm_data = NULL;
1464 streaming_msg_free(sm2);
1465 }
1466 dvr_thread_mpegts_stats(de, sm->sm_data);
1467 muxer_write_pkt(prch->prch_muxer, sm->sm_type, sm->sm_data);
1468 sm->sm_data = NULL;
1469 dvr_notify(de);
1470 packets++;
1471 break;
1472
1473 case SMT_START:
1474 start_time = gclk();
1475 packets = 0;
1476 if (ss)
1477 streaming_start_unref(ss);
1478 ss = streaming_start_copy((streaming_start_t *)sm->sm_data);
1479 break;
1480
1481 case SMT_STOP:
1482 if (sm->sm_code == SM_CODE_SOURCE_RECONFIGURED) {
1483 // Subscription is restarting, wait for SMT_START
1484 if (muxing)
1485 tvhtrace(LS_DVR, "%s - source reconfigured", idnode_uuid_as_str(&de->de_id, ubuf));
1486 muxing = 0; // reconfigure muxer
1487
1488 } else if(sm->sm_code == 0) {
1489 // Recording is completed
1490
1491 dvr_entry_set_state(de, de->de_sched_state, de->de_rec_state, SM_CODE_OK);
1492 tvhinfo(LS_DVR, "Recording completed: \"%s\"",
1493 dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL));
1494
1495 goto fin;
1496
1497 } else if (de->de_last_error != sm->sm_code) {
1498 // Error during recording
1499
1500 dvr_rec_set_state(de, DVR_RS_ERROR, sm->sm_code);
1501 tvherror(LS_DVR, "Recording stopped: \"%s\": %s",
1502 dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL),
1503 streaming_code2txt(sm->sm_code));
1504
1505 fin:
1506 streaming_queue_clear(&backlog);
1507 if (!dvr_thread_global_lock(de, &run))
1508 break;
1509 dvr_thread_epilog(de, postproc);
1510 dvr_thread_global_unlock(de);
1511 start_time = 0;
1512 started = 0;
1513 muxing = 0;
1514 if (ss) {
1515 streaming_start_unref(ss);
1516 ss = NULL;
1517 }
1518 }
1519 break;
1520
1521 case SMT_SERVICE_STATUS:
1522 if (sm->sm_code & TSS_PACKETS) {
1523
1524 } else if (sm->sm_code & TSS_ERRORS) {
1525
1526 int code = tss2errcode(sm->sm_code);
1527
1528 if(de->de_last_error != code) {
1529 dvr_rec_set_state(de, DVR_RS_ERROR, code);
1530 tvherror(LS_DVR, "Streaming error: \"%s\": %s",
1531 dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL),
1532 streaming_code2txt(code));
1533 }
1534 }
1535 break;
1536
1537 case SMT_NOSTART:
1538
1539 if (de->de_last_error != sm->sm_code) {
1540 dvr_rec_set_state(de, DVR_RS_PENDING, sm->sm_code);
1541
1542 tvherror(LS_DVR, "Recording unable to start: \"%s\": %s",
1543 dvr_get_filename(de) ?: lang_str_get(de->de_title, NULL),
1544 streaming_code2txt(sm->sm_code));
1545 }
1546 break;
1547
1548 case SMT_GRACE:
1549 case SMT_NOSTART_WARN:
1550 case SMT_SPEED:
1551 case SMT_SKIP:
1552 case SMT_SIGNAL_STATUS:
1553 case SMT_TIMESHIFT_STATUS:
1554 case SMT_DESCRAMBLE_INFO:
1555 break;
1556
1557 case SMT_EXIT:
1558 run = 0;
1559 break;
1560 }
1561
1562 streaming_msg_free(sm);
1563 pthread_mutex_lock(&sq->sq_mutex);
1564 }
1565 pthread_mutex_unlock(&sq->sq_mutex);
1566
1567 streaming_queue_clear(&backlog);
1568
1569 if (ss)
1570 streaming_start_unref(ss);
1571
1572 return postproc;
1573 }
1574
1575 /**
1576 *
1577 */
1578 void
dvr_spawn_cmd(dvr_entry_t * de,const char * cmd,const char * filename,int pre)1579 dvr_spawn_cmd(dvr_entry_t *de, const char *cmd, const char *filename, int pre)
1580 {
1581 char buf1[MAX(PATH_MAX, 2048)], *buf2;
1582 char tmp[MAX(PATH_MAX, 512)];
1583 htsmsg_t *info = NULL, *e;
1584 htsmsg_field_t *f;
1585 char **args;
1586
1587 if (!pre) {
1588 if ((f = htsmsg_field_last(de->de_files)) != NULL &&
1589 (e = htsmsg_field_get_map(f)) != NULL) {
1590 if (filename == NULL) {
1591 filename = htsmsg_get_str(e, "filename");
1592 if (filename == NULL)
1593 return;
1594 }
1595 info = htsmsg_get_list(e, "info");
1596 } else {
1597 return;
1598 }
1599 }
1600
1601 /* Substitute DVR entry formatters */
1602 htsstr_substitute(cmd, buf1, sizeof(buf1), '%', dvr_subs_postproc_entry, de, tmp, sizeof(tmp));
1603 buf2 = tvh_strdupa(buf1);
1604 /* Substitute filename formatters */
1605 if (!pre) {
1606 htsstr_substitute(buf2, buf1, sizeof(buf1), '%', dvr_subs_postproc_filename, filename, tmp, sizeof(tmp));
1607 buf2 = tvh_strdupa(buf1);
1608 }
1609 /* Substitute info formatters */
1610 if (info)
1611 htsstr_substitute(buf2, buf1, sizeof(buf1), '%', dvr_subs_postproc_info, info, tmp, sizeof(tmp));
1612
1613 args = htsstr_argsplit(buf1);
1614 if(args[0])
1615 spawnv(args[0], (void *)args, NULL, 1, 1);
1616
1617 htsstr_argsplit_free(args);
1618 }
1619
1620 /**
1621 *
1622 */
1623 static void
dvr_thread_epilog(dvr_entry_t * de,const char * dvr_postproc)1624 dvr_thread_epilog(dvr_entry_t *de, const char *dvr_postproc)
1625 {
1626 profile_chain_t *prch = de->de_chain;
1627 htsmsg_t *e;
1628 htsmsg_field_t *f;
1629
1630 lock_assert(&global_lock);
1631
1632 if (prch == NULL)
1633 return;
1634
1635 muxer_close(prch->prch_muxer);
1636 muxer_destroy(prch->prch_muxer);
1637 prch->prch_muxer = NULL;
1638
1639 if ((f = htsmsg_field_last(de->de_files)) != NULL &&
1640 (e = htsmsg_field_get_map(f)) != NULL)
1641 htsmsg_set_s64(e, "stop", gclk());
1642
1643 if(dvr_postproc && dvr_postproc[0])
1644 dvr_spawn_cmd(de, dvr_postproc, NULL, 0);
1645
1646 idnode_changed(&de->de_id);
1647 }
1648