1 /*
2  *  tvheadend, Stream Profile
3  *  Copyright (C) 2014 Jaroslav Kysela
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "tvheadend.h"
20 #include "settings.h"
21 #include "profile.h"
22 #include "streaming.h"
23 #include "access.h"
24 #include "plumbing/tsfix.h"
25 #include "plumbing/globalheaders.h"
26 #if ENABLE_LIBAV
27 #include "lang_codes.h"
28 #include "plumbing/transcoding.h"
29 #endif
30 #if ENABLE_TIMESHIFT
31 #include "timeshift.h"
32 #endif
33 #include "dvr/dvr.h"
34 
35 extern const idclass_t profile_htsp_class;
36 
37 profile_builders_queue profile_builders;
38 
39 struct profile_entry_queue profiles;
40 static LIST_HEAD(,profile_chain) profile_chains;
41 
42 static profile_t *profile_default;
43 
44 /*
45  *
46  */
47 
48 void
profile_register(const idclass_t * clazz,profile_builder_t builder)49 profile_register(const idclass_t *clazz, profile_builder_t builder)
50 {
51   profile_build_t *pb = calloc(1, sizeof(*pb)), *pb2;
52   idclass_register(clazz);
53   pb->clazz = clazz;
54   pb->build = builder;
55   pb2 = LIST_FIRST(&profile_builders);
56   if (pb2) {
57     /* append tail */
58     while (LIST_NEXT(pb2, link))
59       pb2 = LIST_NEXT(pb2, link);
60     LIST_INSERT_AFTER(pb2, pb, link);
61   } else {
62     LIST_INSERT_HEAD(&profile_builders, pb, link);
63   }
64 }
65 
66 static profile_build_t *
profile_class_find(const char * name)67 profile_class_find(const char *name)
68 {
69   profile_build_t *pb;
70   LIST_FOREACH(pb, &profile_builders, link) {
71     if (strcmp(pb->clazz->ic_class, name) == 0)
72       return pb;
73   }
74   return NULL;
75 }
76 
77 profile_t *
profile_create(const char * uuid,htsmsg_t * conf,int save)78 profile_create
79   (const char *uuid, htsmsg_t *conf, int save)
80 {
81   profile_t *pro = NULL;
82   profile_build_t *pb = NULL;
83   const char *s;
84 
85   lock_assert(&global_lock);
86 
87   if ((s = htsmsg_get_str(conf, "class")) != NULL)
88     pb = profile_class_find(s);
89   if (pb == NULL) {
90     tvherror(LS_PROFILE, "wrong class %s!", s);
91     return NULL;
92   }
93   pro = pb->build();
94   if (pro == NULL) {
95     tvherror(LS_PROFILE, "Profile class %s is not available!", s);
96     return NULL;
97   }
98   LIST_INIT(&pro->pro_dvr_configs);
99   LIST_INIT(&pro->pro_accesses);
100   pro->pro_swservice = 1;
101   pro->pro_contaccess = 1;
102   pro->pro_ca_timeout = 2000;
103   if (idnode_insert(&pro->pro_id, uuid, pb->clazz, 0)) {
104     if (uuid)
105       tvherror(LS_PROFILE, "invalid uuid '%s'", uuid);
106     free(pro);
107     return NULL;
108   }
109   if (conf) {
110     int b;
111     idnode_load(&pro->pro_id, conf);
112     if (!htsmsg_get_bool(conf, "shield", &b))
113       pro->pro_shield = !!b;
114   }
115   pro->pro_refcount = 1;
116   TAILQ_INSERT_TAIL(&profiles, pro, pro_link);
117   if (save)
118     idnode_changed(&pro->pro_id);
119   if (pro->pro_conf_changed)
120     pro->pro_conf_changed(pro);
121   return pro;
122 }
123 
124 void
profile_release_(profile_t * pro)125 profile_release_(profile_t *pro)
126 {
127   if (pro->pro_free)
128     pro->pro_free(pro);
129   free(pro->pro_name);
130   free(pro->pro_comment);
131   free(pro);
132 }
133 
134 static void
profile_delete(profile_t * pro,int delconf)135 profile_delete(profile_t *pro, int delconf)
136 {
137   char ubuf[UUID_HEX_SIZE];
138   idnode_save_check(&pro->pro_id, delconf);
139   pro->pro_enabled = 0;
140   if (pro->pro_conf_changed)
141     pro->pro_conf_changed(pro);
142   if (delconf)
143     hts_settings_remove("profile/%s", idnode_uuid_as_str(&pro->pro_id, ubuf));
144   TAILQ_REMOVE(&profiles, pro, pro_link);
145   idnode_unlink(&pro->pro_id);
146   dvr_config_destroy_by_profile(pro, delconf);
147   access_destroy_by_profile(pro, delconf);
148   profile_release(pro);
149 }
150 
151 static htsmsg_t *
profile_class_save(idnode_t * in,char * filename,size_t fsize)152 profile_class_save ( idnode_t *in, char *filename, size_t fsize )
153 {
154   profile_t *pro = (profile_t *)in;
155   htsmsg_t *c = htsmsg_create_map();
156   char ubuf[UUID_HEX_SIZE];
157   if (pro == profile_default)
158     pro->pro_enabled = 1;
159   idnode_save(in, c);
160   if (pro->pro_shield)
161     htsmsg_add_bool(c, "shield", 1);
162   snprintf(filename, fsize, "profile/%s", idnode_uuid_as_str(in, ubuf));
163   if (pro->pro_conf_changed)
164     pro->pro_conf_changed(pro);
165   return c;
166 }
167 
168 static const char *
profile_class_get_title(idnode_t * in,const char * lang)169 profile_class_get_title ( idnode_t *in, const char *lang )
170 {
171   profile_t *pro = (profile_t *)in;
172   if (pro->pro_name && pro->pro_name[0])
173     return pro->pro_name;
174   snprintf(prop_sbuf, sizeof(prop_sbuf), "%s", idclass_get_caption(in->in_class, lang));
175   return prop_sbuf;
176 }
177 
178 static void
profile_class_delete(idnode_t * self)179 profile_class_delete(idnode_t *self)
180 {
181   profile_t *pro = (profile_t *)self;
182   if (pro->pro_shield)
183     return;
184   profile_delete(pro, 1);
185 }
186 
187 static uint32_t
profile_class_enabled_opts(void * o)188 profile_class_enabled_opts(void *o)
189 {
190   profile_t *pro = o;
191   uint32_t r = 0;
192   if (pro && profile_default == pro)
193     r |= PO_RDONLY;
194   return r;
195 }
196 
197 static const void *
profile_class_class_get(void * o)198 profile_class_class_get(void *o)
199 {
200   profile_t *pro = o;
201   static const char *ret;
202   ret = pro->pro_id.in_class->ic_class;
203   return &ret;
204 }
205 
206 static int
profile_class_class_set(void * o,const void * v)207 profile_class_class_set(void *o, const void *v)
208 {
209   /* just ignore, create fcn does the right job */
210   return 0;
211 }
212 
213 static const void *
profile_class_default_get(void * o)214 profile_class_default_get(void *o)
215 {
216   static int res;
217   res = o == profile_default;
218   return &res;
219 }
220 
221 static int
profile_class_default_set(void * o,const void * v)222 profile_class_default_set(void *o, const void *v)
223 {
224   profile_t *pro = o, *old;
225   if (*(int *)v && pro != profile_default) {
226     old = profile_default;
227     profile_default = pro;
228     if (old)
229       idnode_changed(&old->pro_id);
230     return 1;
231   }
232   return 0;
233 }
234 
235 static uint32_t
profile_class_name_opts(void * o)236 profile_class_name_opts(void *o)
237 {
238   profile_t *pro = o;
239   uint32_t r = 0;
240   if (pro && pro->pro_shield)
241     r |= PO_RDONLY;
242   return r;
243 }
244 
245 static htsmsg_t *
profile_class_priority_list(void * o,const char * lang)246 profile_class_priority_list ( void *o, const char *lang )
247 {
248   static const struct strtab tab[] = {
249     { N_("Unset (default)"),          PROFILE_SPRIO_NOTSET },
250     { N_("Important"),                PROFILE_SPRIO_IMPORTANT },
251     { N_("High"),                     PROFILE_SPRIO_HIGH, },
252     { N_("Normal"),                   PROFILE_SPRIO_NORMAL },
253     { N_("Low"),                      PROFILE_SPRIO_LOW },
254     { N_("Unimportant"),              PROFILE_SPRIO_UNIMPORTANT },
255     { N_("DVR override: important"),   PROFILE_SPRIO_DVR_IMPORTANT },
256     { N_("DVR override: high"),        PROFILE_SPRIO_DVR_HIGH },
257     { N_("DVR override: normal"),      PROFILE_SPRIO_DVR_NORMAL },
258     { N_("DVR override: low"),         PROFILE_SPRIO_DVR_LOW },
259     { N_("DVR override: unimportant"), PROFILE_SPRIO_DVR_UNIMPORTANT },
260   };
261   return strtab2htsmsg(tab, 1, lang);
262 }
263 
264 static htsmsg_t *
profile_class_svfilter_list(void * o,const char * lang)265 profile_class_svfilter_list ( void *o, const char *lang )
266 {
267   static const struct strtab tab[] = {
268     { N_("None"),                       PROFILE_SVF_NONE },
269     { N_("SD: standard definition"),    PROFILE_SVF_SD },
270     { N_("HD: high definition"),        PROFILE_SVF_HD },
271     { N_("UHD: ultra high definition"), PROFILE_SVF_UHD },
272   };
273   return strtab2htsmsg(tab, 1, lang);
274 }
275 
276 CLASS_DOC(profile)
277 
278 const idclass_t profile_class =
279 {
280   .ic_class      = "profile",
281   .ic_caption    = N_("Stream Profile"),
282   .ic_event      = "profile",
283   .ic_doc        = tvh_doc_profile_class,
284   .ic_perm_def   = ACCESS_ADMIN,
285   .ic_save       = profile_class_save,
286   .ic_get_title  = profile_class_get_title,
287   .ic_delete     = profile_class_delete,
288   .ic_groups     = (const property_group_t[]) {
289     {
290       .name   = N_("Configuration"),
291       .number = 1,
292     },
293     {}
294   },
295   .ic_properties = (const property_t[]){
296     {
297       .type     = PT_STR,
298       .id       = "class",
299       .name     = N_("Class"),
300       .opts     = PO_RDONLY | PO_HIDDEN,
301       .get      = profile_class_class_get,
302       .set      = profile_class_class_set,
303       .group    = 1
304     },
305     {
306       .type     = PT_BOOL,
307       .id       = "enabled",
308       .name     = N_("Enabled"),
309       .desc     = N_("Enable/disable profile."),
310       .off      = offsetof(profile_t, pro_enabled),
311       .get_opts = profile_class_enabled_opts,
312       .group    = 1,
313       .def.i    = 1
314     },
315     {
316       .type     = PT_BOOL,
317       .id       = "default",
318       .name     = N_("Default"),
319       .desc     = N_("Set as default profile."),
320       .set      = profile_class_default_set,
321       .get      = profile_class_default_get,
322       .opts     = PO_EXPERT,
323       .group    = 1
324     },
325     {
326       .type     = PT_STR,
327       .id       = "name",
328       .name     = N_("Profile name"),
329       .desc     = N_("The name of the profile."),
330       .off      = offsetof(profile_t, pro_name),
331       .get_opts = profile_class_name_opts,
332       .notify   = idnode_notify_title_changed,
333       .group    = 1
334     },
335     {
336       .type     = PT_STR,
337       .id       = "comment",
338       .name     = N_("Comment"),
339       .desc     = N_("Free-form text field. You can enter whatever you "
340                      "like here."),
341       .off      = offsetof(profile_t, pro_comment),
342       .group    = 1
343     },
344     {
345       .type     = PT_INT,
346       .id       = "priority",
347       .name     = N_("Default priority"),
348       .desc     = N_("If no specific priority was requested. This "
349                      "gives certain users a higher priority by "
350                      "assigning a streaming profile with a higher "
351                      "priority."),
352       .list     = profile_class_priority_list,
353       .off      = offsetof(profile_t, pro_prio),
354       .opts     = PO_SORTKEY | PO_ADVANCED,
355       .def.i    = PROFILE_SPRIO_NORMAL,
356       .group    = 1
357     },
358     {
359       .type     = PT_INT,
360       .id       = "fpriority",
361       .name     = N_("Force priority"),
362       .desc     = N_("Force profile to use this priority."),
363       .off      = offsetof(profile_t, pro_fprio),
364       .opts     = PO_EXPERT,
365       .group    = 1
366     },
367     {
368       .type     = PT_INT,
369       .id       = "timeout",
370       .name     = N_("Timeout (sec) (0=infinite)"),
371       .desc     = N_("The number of seconds to wait for a stream to "
372                      "start."),
373       .off      = offsetof(profile_t, pro_timeout),
374       .def.i    = 5,
375       .group    = 1
376     },
377     {
378       .type     = PT_BOOL,
379       .id       = "restart",
380       .name     = N_("Restart on error"),
381       .desc     = N_("Restart streaming on error. This is useful for "
382                      "DVR so a recording isn't aborted if an error occurs."),
383       .off      = offsetof(profile_t, pro_restart),
384       .opts     = PO_EXPERT,
385       .def.i    = 0,
386       .group    = 1
387     },
388     {
389       .type     = PT_BOOL,
390       .id       = "contaccess",
391       .name     = N_("Continue if descrambling fails"),
392       .desc     = N_("Don't abort streaming when an encrypted stream "
393                      "can't be decrypted by a CA client that normally "
394                      "should be able to decrypt the stream."),
395       .off      = offsetof(profile_t, pro_contaccess),
396       .opts     = PO_EXPERT,
397       .def.i    = 1,
398       .group    = 1
399     },
400     {
401       .type     = PT_INT,
402       .id       = "catimeout",
403       .name     = N_("Descrambling timeout (ms)"),
404       .desc     = N_("Check the descrambling status after this timeout."),
405       .off      = offsetof(profile_t, pro_ca_timeout),
406       .opts     = PO_EXPERT,
407       .def.i    = 2000,
408       .group    = 1
409     },
410     {
411       .type     = PT_BOOL,
412       .id       = "swservice",
413       .name     = N_("Switch to another service"),
414       .desc     = N_("If something fails, try to switch to a different "
415                      "service on another network. Do not try to iterate "
416                      "through all inputs/tuners which are capable to "
417                      "receive the service."),
418       .off      = offsetof(profile_t, pro_swservice),
419       .opts     = PO_EXPERT,
420       .def.i    = 1,
421       .group    = 1
422     },
423     {
424       .type     = PT_INT,
425       .id       = "svfilter",
426       .name     = N_("Preferred service video type"),
427       .desc     = N_("The selected video type should be preferred when "
428                      "multiple services are available for a channel."),
429       .list     = profile_class_svfilter_list,
430       .off      = offsetof(profile_t, pro_svfilter),
431       .opts     = PO_SORTKEY | PO_ADVANCED,
432       .def.i    = PROFILE_SVF_NONE,
433       .group    = 1
434     },
435     { }
436   }
437 };
438 
439 /*
440  *
441  */
442 const char *
profile_get_name(profile_t * pro)443 profile_get_name(profile_t *pro)
444 {
445   if (pro->pro_name && *pro->pro_name) return pro->pro_name;
446   return "";
447 }
448 
449 /*
450  *
451  */
452 static profile_t *
profile_find_by_name2(const char * name,const char * alt,int all)453 profile_find_by_name2(const char *name, const char *alt, int all)
454 {
455   profile_t *pro;
456 
457   lock_assert(&global_lock);
458 
459   if (!name && alt) {
460     name = alt;
461     alt = NULL;
462   }
463 
464   if (!name)
465     return profile_default;
466 
467   TAILQ_FOREACH(pro, &profiles, pro_link) {
468     if ((all || pro->pro_enabled) && !strcmp(profile_get_name(pro), name))
469       return pro;
470   }
471 
472   if (alt) {
473     TAILQ_FOREACH(pro, &profiles, pro_link) {
474       if ((all || pro->pro_enabled) && !strcmp(profile_get_name(pro), alt))
475         return pro;
476     }
477   }
478 
479   return profile_default;
480 }
481 
482 /*
483  *
484  */
485 profile_t *
profile_find_by_name(const char * name,const char * alt)486 profile_find_by_name(const char *name, const char *alt)
487 {
488   return profile_find_by_name2(name, alt, 0);
489 }
490 
491 /*
492  *
493  */
494 int
profile_verify(profile_t * pro,int sflags)495 profile_verify(profile_t *pro, int sflags)
496 {
497   if (!pro)
498     return 0;
499   if (!pro->pro_enabled)
500     return 0;
501   if ((sflags & SUBSCRIPTION_HTSP) != 0 && !pro->pro_work)
502     return 0;
503   if ((sflags & SUBSCRIPTION_HTSP) == 0 && !pro->pro_open)
504     return 0;
505   sflags &= pro->pro_sflags;
506   sflags &= SUBSCRIPTION_PACKET|SUBSCRIPTION_MPEGTS;
507   return sflags ? 1 : 0;
508 }
509 
510 /*
511  *
512  */
513 profile_t *
profile_find_by_list(htsmsg_t * uuids,const char * name,const char * alt,int sflags)514 profile_find_by_list
515   (htsmsg_t *uuids, const char *name, const char *alt, int sflags)
516 {
517   profile_t *pro, *res = NULL;
518   htsmsg_field_t *f;
519   const char *uuid, *uuid2;
520   char ubuf[UUID_HEX_SIZE];
521 
522   pro = profile_find_by_uuid(name);
523   if (!pro)
524     pro = profile_find_by_name(name, alt);
525   if (!profile_verify(pro, sflags))
526     pro = NULL;
527   if (uuids) {
528     uuid = pro ? idnode_uuid_as_str(&pro->pro_id, ubuf) : "";
529     HTSMSG_FOREACH(f, uuids) {
530       uuid2 = htsmsg_field_get_str(f) ?: "";
531       if (strcmp(uuid, uuid2) == 0 && profile_verify(pro, sflags))
532         return pro;
533       if (!res) {
534         res = profile_find_by_uuid(uuid2);
535         if (!profile_verify(res, sflags))
536           res = NULL;
537       }
538     }
539   } else {
540     res = pro;
541   }
542   if (!res) {
543     res = profile_find_by_name((sflags & SUBSCRIPTION_HTSP) ? "htsp" : NULL, NULL);
544     if (!profile_verify(res, sflags))
545       tvherror(LS_PROFILE, "unable to select a working profile (asked '%s' alt '%s')", name, alt);
546   }
547   return res;
548 }
549 
550 /*
551  *
552  */
553 char *
profile_validate_name(const char * name)554 profile_validate_name(const char *name)
555 {
556   profile_t *pro;
557 
558   lock_assert(&global_lock);
559 
560   TAILQ_FOREACH(pro, &profiles, pro_link) {
561     if (name && !strcmp(profile_get_name(pro), name))
562       return strdup(name);
563   }
564 
565   if (profile_default)
566     return strdup(profile_get_name(profile_default));
567 
568   return NULL;
569 }
570 
571 /*
572  *
573  */
574 htsmsg_t *
profile_class_get_list(void * o,const char * lang)575 profile_class_get_list(void *o, const char *lang)
576 {
577   htsmsg_t *m = htsmsg_create_map();
578   htsmsg_t *p = htsmsg_create_map();
579   htsmsg_add_str(m, "type",  "api");
580   htsmsg_add_str(m, "uri",   "profile/list");
581   htsmsg_add_str(m, "event", "profile");
582   htsmsg_add_u32(p, "all",  1);
583   htsmsg_add_msg(m, "params", p);
584   return m;
585 }
586 
587 /*
588  *
589  */
590 void
profile_get_htsp_list(htsmsg_t * array,htsmsg_t * filter)591 profile_get_htsp_list(htsmsg_t *array, htsmsg_t *filter)
592 {
593   profile_t *pro;
594   htsmsg_t *m;
595   htsmsg_field_t *f;
596   const char *uuid, *s;
597   char ubuf[UUID_HEX_SIZE];
598 
599   TAILQ_FOREACH(pro, &profiles, pro_link) {
600     if (!pro->pro_work)
601       continue;
602     uuid = idnode_uuid_as_str(&pro->pro_id, ubuf);
603     if (filter) {
604       HTSMSG_FOREACH(f, filter) {
605         if (!(s = htsmsg_field_get_str(f)))
606           continue;
607         if (strcmp(s, uuid) == 0)
608           break;
609       }
610       if (f == NULL)
611         continue;
612     }
613     m = htsmsg_create_map();
614     htsmsg_add_str(m, "uuid", uuid);
615     htsmsg_add_str(m, "name", pro->pro_name ?: "");
616     htsmsg_add_str(m, "comment", pro->pro_comment ?: "");
617     htsmsg_add_msg(array, NULL, m);
618   }
619 }
620 
621 /*
622  *
623  */
624 static void
profile_deliver(profile_chain_t * prch,streaming_message_t * sm)625 profile_deliver(profile_chain_t *prch, streaming_message_t *sm)
626 {
627   if (prch->prch_start_pending) {
628     profile_sharer_t *prsh = prch->prch_sharer;
629     streaming_message_t *sm2;
630     if (!prsh->prsh_start_msg) {
631       streaming_msg_free(sm);
632       return;
633     }
634     sm2 = streaming_msg_create_data(SMT_START,
635                                    streaming_start_copy(prsh->prsh_start_msg));
636     streaming_target_deliver(prch->prch_post_share, sm2);
637     prch->prch_start_pending = 0;
638   }
639   if (sm)
640     streaming_target_deliver(prch->prch_post_share, sm);
641 }
642 
643 /*
644  *
645  */
646 static void
profile_input(void * opaque,streaming_message_t * sm)647 profile_input(void *opaque, streaming_message_t *sm)
648 {
649   profile_chain_t *prch = opaque, *prch2;
650   profile_sharer_t *prsh = prch->prch_sharer;
651 
652   if (sm->sm_type == SMT_START) {
653     if (!prsh->prsh_master)
654       prsh->prsh_master = prch;
655     prch->prch_stop = 0;
656   }
657 
658   if (prch == prsh->prsh_master) {
659     if (sm->sm_type == SMT_STOP) {
660       prch->prch_stop = 1;
661       /* elect new master */
662       prsh->prsh_master = NULL;
663       LIST_FOREACH(prch2, &prsh->prsh_chains, prch_sharer_link)
664         if (!prch2->prch_stop) {
665           prsh->prsh_master = prch2;
666           break;
667         }
668       if (prsh->prsh_master)
669         goto direct;
670     }
671     streaming_target_deliver(prch->prch_share, sm);
672     return;
673   }
674 
675   if (sm->sm_type == SMT_STOP) {
676     prch->prch_stop = 1;
677   } else if (sm->sm_type == SMT_START) {
678     prch->prch_stop = 0;
679     prch->prch_start_pending = 1;
680     streaming_msg_free(sm);
681     sm = NULL;
682   } else if (sm->sm_type == SMT_PACKET || sm->sm_type == SMT_MPEGTS) {
683     streaming_msg_free(sm);
684     return;
685   }
686 
687 direct:
688   profile_deliver(prch, sm);
689 }
690 
691 static htsmsg_t *
profile_input_info(void * opaque,htsmsg_t * list)692 profile_input_info(void *opaque, htsmsg_t *list)
693 {
694   profile_chain_t *prch = opaque;
695   streaming_target_t *st = prch->prch_share;
696   htsmsg_add_str(list, NULL, "profile input");
697   st->st_ops.st_info(st->st_opaque, list);
698   st = prch->prch_post_share;
699   return st->st_ops.st_info(st->st_opaque, list);
700 }
701 
702 static streaming_ops_t profile_input_ops = {
703   .st_cb   = profile_input,
704   .st_info = profile_input_info
705 };
706 
707 /*
708  *
709  */
710 static void
profile_sharer_deliver(profile_chain_t * prch,streaming_message_t * sm)711 profile_sharer_deliver(profile_chain_t *prch, streaming_message_t *sm)
712 {
713   if (sm->sm_type == SMT_PACKET) {
714     if (!prch->prch_ts_delta)
715       goto deliver;
716     th_pkt_t *pkt = sm->sm_data;
717     if (prch->prch_ts_delta == PTS_UNSET)
718       prch->prch_ts_delta = MAX(0, pkt->pkt_dts - 10000);
719     /*
720      * time correction here
721      */
722     if (pkt->pkt_pts >= prch->prch_ts_delta &&
723         pkt->pkt_dts >= prch->prch_ts_delta) {
724       th_pkt_t *n = pkt_copy_shallow(pkt);
725       pkt_ref_dec(pkt);
726       n->pkt_pts -= prch->prch_ts_delta;
727       n->pkt_dts -= prch->prch_ts_delta;
728       sm->sm_data = n;
729     } else {
730       streaming_msg_free(sm);
731       return;
732     }
733   }
734 deliver:
735   profile_deliver(prch, sm);
736 }
737 
738 /*
739  *
740  */
741 static void
profile_sharer_input(void * opaque,streaming_message_t * sm)742 profile_sharer_input(void *opaque, streaming_message_t *sm)
743 {
744   profile_sharer_t *prsh = opaque;
745   profile_chain_t *prch, *next, *run = NULL;
746 
747   if (sm->sm_type == SMT_STOP) {
748     if (prsh->prsh_start_msg)
749       streaming_start_unref(prsh->prsh_start_msg);
750     prsh->prsh_start_msg = NULL;
751   }
752   for (prch = LIST_FIRST(&prsh->prsh_chains); prch; prch = next) {
753     next = LIST_NEXT(prch, prch_sharer_link);
754     if (prch == prsh->prsh_master) {
755       if (sm->sm_type == SMT_START) {
756         if (prsh->prsh_start_msg)
757           streaming_start_unref(prsh->prsh_start_msg);
758         prsh->prsh_start_msg = streaming_start_copy(sm->sm_data);
759       }
760       if (run)
761         profile_sharer_deliver(run, streaming_msg_clone(sm));
762       run = prch;
763       continue;
764     } else if (sm->sm_type == SMT_STOP) {
765       run = prch;
766       continue;
767     }
768     if (sm->sm_type != SMT_PACKET && sm->sm_type != SMT_MPEGTS)
769       continue;
770     if (prch->prch_stop)
771       continue;
772     if (run)
773       profile_sharer_deliver(run, streaming_msg_clone(sm));
774     run = prch;
775   }
776 
777   if (run)
778     profile_sharer_deliver(run, sm);
779   else
780     streaming_msg_free(sm);
781 }
782 
783 static htsmsg_t *
profile_sharer_input_info(void * opaque,htsmsg_t * list)784 profile_sharer_input_info(void *opaque, htsmsg_t *list)
785 {
786   htsmsg_add_str(list, NULL, "profile sharer input");
787   return list;
788 }
789 
790 static streaming_ops_t profile_sharer_input_ops = {
791   .st_cb   = profile_sharer_input,
792   .st_info = profile_sharer_input_info
793 };
794 
795 /*
796  *
797  */
798 static profile_sharer_t *
profile_sharer_find(profile_chain_t * prch)799 profile_sharer_find(profile_chain_t *prch)
800 {
801   profile_sharer_t *prsh = NULL;
802   profile_chain_t *prch2;
803 
804   LIST_FOREACH(prch2, &profile_chains, prch_link) {
805     if (prch2->prch_id != prch->prch_id)
806       continue;
807     if (prch2 == prch)
808       continue;
809     if (prch2->prch_can_share && prch2->prch_can_share(prch2, prch)) {
810       prsh = prch2->prch_sharer;
811       break;
812     }
813   }
814   if (!prsh) {
815     prsh = calloc(1, sizeof(*prsh));
816     streaming_target_init(&prsh->prsh_input, &profile_sharer_input_ops, prsh, 0);
817     LIST_INIT(&prsh->prsh_chains);
818   }
819   return prsh;
820 }
821 
822 /*
823  *
824  */
825 static int
profile_sharer_create(profile_sharer_t * prsh,profile_chain_t * prch,streaming_target_t * dst)826 profile_sharer_create(profile_sharer_t *prsh,
827                       profile_chain_t *prch,
828                       streaming_target_t *dst)
829 {
830   prch->prch_post_share = dst;
831   prch->prch_ts_delta = LIST_EMPTY(&prsh->prsh_chains) ? 0 : PTS_UNSET;
832   LIST_INSERT_HEAD(&prsh->prsh_chains, prch, prch_sharer_link);
833   prch->prch_sharer = prsh;
834   if (!prsh->prsh_master)
835     prsh->prsh_master = prch;
836   return 0;
837 }
838 
839 /*
840  *
841  */
842 static void
profile_sharer_destroy(profile_chain_t * prch)843 profile_sharer_destroy(profile_chain_t *prch)
844 {
845   profile_sharer_t *prsh = prch->prch_sharer;
846 
847   if (prsh == NULL)
848     return;
849   LIST_REMOVE(prch, prch_sharer_link);
850   prch->prch_sharer = NULL;
851   prch->prch_post_share = NULL;
852   if (LIST_EMPTY(&prsh->prsh_chains)) {
853     if (prsh->prsh_tsfix)
854       tsfix_destroy(prsh->prsh_tsfix);
855 #if ENABLE_LIBAV
856     if (prsh->prsh_transcoder)
857       transcoder_destroy(prsh->prsh_transcoder);
858 #endif
859     if (prsh->prsh_start_msg)
860       streaming_start_unref(prsh->prsh_start_msg);
861     free(prsh);
862   }
863 }
864 
865 /*
866  *
867  */
868 void
profile_chain_init(profile_chain_t * prch,profile_t * pro,void * id,int queue)869 profile_chain_init(profile_chain_t *prch, profile_t *pro, void *id, int queue)
870 {
871   memset(prch, 0, sizeof(*prch));
872   if (pro)
873     profile_grab(pro);
874   prch->prch_pro = pro;
875   prch->prch_id  = id;
876   if (queue) {
877     streaming_queue_init(&prch->prch_sq, 0, 0);
878     prch->prch_sq_used = 1;
879   }
880   LIST_INSERT_HEAD(&profile_chains, prch, prch_link);
881   prch->prch_linked = 1;
882   prch->prch_stop = 1;
883 }
884 
885 /*
886  *
887  */
888 int
profile_chain_work(profile_chain_t * prch,struct streaming_target * dst,uint32_t timeshift_period,int flags)889 profile_chain_work(profile_chain_t *prch, struct streaming_target *dst,
890                    uint32_t timeshift_period, int flags)
891 {
892   profile_t *pro = prch->prch_pro;
893   if (pro && pro->pro_work)
894     return pro->pro_work(prch, dst, timeshift_period, flags);
895   return -1;
896 }
897 
898 /*
899  *
900  */
901 int
profile_chain_reopen(profile_chain_t * prch,muxer_config_t * m_cfg,int flags)902 profile_chain_reopen(profile_chain_t *prch,
903                      muxer_config_t *m_cfg, int flags)
904 {
905   profile_t *pro = prch->prch_pro;
906   if (pro && pro->pro_reopen)
907     return pro->pro_reopen(prch, m_cfg, flags);
908   return -1;
909 }
910 
911 /*
912  *
913  */
914 int
profile_chain_open(profile_chain_t * prch,muxer_config_t * m_cfg,int flags,size_t qsize)915 profile_chain_open(profile_chain_t *prch,
916                    muxer_config_t *m_cfg, int flags, size_t qsize)
917 {
918   profile_t *pro = prch->prch_pro;
919   if (pro && pro->pro_open)
920     return pro->pro_open(prch, m_cfg, flags, qsize);
921   return -1;
922 }
923 
924 /*
925  *
926  */
927 int
profile_chain_raw_open(profile_chain_t * prch,void * id,size_t qsize,int muxer)928 profile_chain_raw_open(profile_chain_t *prch, void *id, size_t qsize, int muxer)
929 {
930   muxer_config_t c;
931 
932   memset(prch, 0, sizeof(*prch));
933   prch->prch_id    = id;
934   prch->prch_flags = SUBSCRIPTION_MPEGTS;
935   streaming_queue_init(&prch->prch_sq, SMT_PACKET, qsize);
936   prch->prch_sq_used = 1;
937   prch->prch_st    = &prch->prch_sq.sq_st;
938   if (muxer) {
939     memset(&c, 0, sizeof(c));
940     c.m_type = MC_RAW;
941     prch->prch_muxer = muxer_create(&c);
942   }
943   return 0;
944 }
945 
946 /*
947  *
948  */
949 
950 static const int prio2weight[] = {
951   [PROFILE_SPRIO_DVR_IMPORTANT]   = 525,
952   [PROFILE_SPRIO_DVR_HIGH]        = 425,
953   [PROFILE_SPRIO_DVR_NORMAL]      = 325,
954   [PROFILE_SPRIO_DVR_LOW]         = 225,
955   [PROFILE_SPRIO_DVR_UNIMPORTANT] = 175,
956   [PROFILE_SPRIO_IMPORTANT]       = 150,
957   [PROFILE_SPRIO_HIGH]            = 125,
958   [PROFILE_SPRIO_NORMAL]          = 100,
959   [PROFILE_SPRIO_LOW]             = 75,
960   [PROFILE_SPRIO_UNIMPORTANT]     = 50,
961   [PROFILE_SPRIO_NOTSET]          = 0
962 };
963 
profile_chain_weight(profile_chain_t * prch,int custom)964 int profile_chain_weight(profile_chain_t *prch, int custom)
965 {
966   int w, w2;
967 
968   w = 100;
969   if (prch->prch_pro) {
970     if (!prch->prch_pro->pro_fprio && custom > 0)
971       return custom;
972     if (idnode_is_instance(&prch->prch_pro->pro_id, &profile_htsp_class))
973       w = 150;
974     w2 = prch->prch_pro->pro_prio;
975     if (w2 > 0 && w2 < ARRAY_SIZE(prio2weight))
976        w = prio2weight[w2];
977   } else {
978     if (custom > 0)
979       return custom;
980   }
981   return w;
982 }
983 
984 /*
985  *
986  */
987 void
profile_chain_close(profile_chain_t * prch)988 profile_chain_close(profile_chain_t *prch)
989 {
990   if (prch == NULL)
991     return;
992 
993   profile_sharer_destroy(prch);
994 
995 #if ENABLE_TIMESHIFT
996   if (prch->prch_timeshift) {
997     timeshift_destroy(prch->prch_timeshift);
998     prch->prch_timeshift = NULL;
999   }
1000 #endif
1001   if (prch->prch_gh) {
1002     globalheaders_destroy(prch->prch_gh);
1003     prch->prch_gh = NULL;
1004   }
1005   if (prch->prch_tsfix) {
1006     tsfix_destroy(prch->prch_tsfix);
1007     prch->prch_tsfix = NULL;
1008   }
1009   if (prch->prch_muxer) {
1010     muxer_destroy(prch->prch_muxer);
1011     prch->prch_muxer = NULL;
1012   }
1013 
1014   prch->prch_st = NULL;
1015 
1016   if (prch->prch_sq_used) {
1017     streaming_queue_deinit(&prch->prch_sq);
1018     prch->prch_sq_used = 0;
1019   }
1020 
1021   if (prch->prch_linked) {
1022     LIST_REMOVE(prch, prch_link);
1023     prch->prch_linked = 0;
1024   }
1025 
1026   if (prch->prch_pro) {
1027     profile_release(prch->prch_pro);
1028     prch->prch_pro = NULL;
1029   }
1030 
1031   prch->prch_id = NULL;
1032 }
1033 
1034 /*
1035  *  HTSP Profile Class
1036  */
1037 const idclass_t profile_htsp_class =
1038 {
1039   .ic_super      = &profile_class,
1040   .ic_class      = "profile-htsp",
1041   .ic_caption    = N_("HTSP Stream Profile"),
1042   .ic_properties = (const property_t[]){
1043     /* Ready for future extensions */
1044     { }
1045   }
1046 };
1047 
1048 static int
profile_htsp_work(profile_chain_t * prch,streaming_target_t * dst,uint32_t timeshift_period,int flags)1049 profile_htsp_work(profile_chain_t *prch,
1050                   streaming_target_t *dst,
1051                   uint32_t timeshift_period, int flags)
1052 {
1053   profile_sharer_t *prsh;
1054 
1055   prsh = profile_sharer_find(prch);
1056   if (!prsh)
1057     goto fail;
1058 
1059 #if ENABLE_TIMESHIFT
1060   if (timeshift_period > 0)
1061     dst = prch->prch_timeshift = timeshift_create(dst, timeshift_period);
1062 #endif
1063 
1064   dst = prch->prch_gh = globalheaders_create(dst);
1065 
1066   if (profile_sharer_create(prsh, prch, dst))
1067     goto fail;
1068 
1069   if (!prsh->prsh_tsfix)
1070     prsh->prsh_tsfix = tsfix_create(&prsh->prsh_input);
1071 
1072   prch->prch_share = prsh->prsh_tsfix;
1073   prch->prch_flags = SUBSCRIPTION_PACKET;
1074   streaming_target_init(&prch->prch_input, &profile_input_ops, prch, 0);
1075   prch->prch_st = &prch->prch_input;
1076   return 0;
1077 
1078 fail:
1079   profile_chain_close(prch);
1080   return -1;
1081 }
1082 
1083 static profile_t *
profile_htsp_builder(void)1084 profile_htsp_builder(void)
1085 {
1086   profile_t *pro = calloc(1, sizeof(*pro));
1087   pro->pro_sflags = SUBSCRIPTION_PACKET;
1088   pro->pro_work   = profile_htsp_work;
1089   return pro;
1090 }
1091 
1092 /*
1093  *  MPEG-TS passthrough muxer
1094  */
1095 typedef struct profile_mpegts {
1096   profile_t;
1097   int pro_rewrite_pmt;
1098   int pro_rewrite_pat;
1099   int pro_rewrite_sdt;
1100   int pro_rewrite_eit;
1101 } profile_mpegts_t;
1102 
1103 const idclass_t profile_mpegts_pass_class =
1104 {
1105   .ic_super      = &profile_class,
1106   .ic_class      = "profile-mpegts",
1107   .ic_caption    = N_("MPEG-TS Pass-thru/built-in"),
1108   .ic_groups     = (const property_group_t[]) {
1109     {
1110       .name   = N_("Configuration"),
1111       .number = 1,
1112     },
1113     {
1114       .name   = N_("Rewrite MPEG-TS SI tables"),
1115       .number = 2,
1116     },
1117     {}
1118   },
1119   .ic_properties = (const property_t[]){
1120     {
1121       .type     = PT_BOOL,
1122       .id       = "rewrite_pmt",
1123       .name     = N_("Rewrite PMT"),
1124       .desc     = N_("Rewrite PMT (Program Map Table) packets to only "
1125                      "include information about the currently-streamed "
1126                      "service."),
1127       .off      = offsetof(profile_mpegts_t, pro_rewrite_pmt),
1128       .opts     = PO_EXPERT,
1129       .def.i    = 1,
1130       .group    = 2
1131     },
1132     {
1133       .type     = PT_BOOL,
1134       .id       = "rewrite_pat",
1135       .name     = N_("Rewrite PAT"),
1136       .desc     = N_("Rewrite PAT (Program Association Table) packets "
1137                      "to only include information about the currently-"
1138                      "streamed service."),
1139       .off      = offsetof(profile_mpegts_t, pro_rewrite_pat),
1140       .opts     = PO_EXPERT,
1141       .def.i    = 1,
1142       .group    = 2
1143     },
1144     {
1145       .type     = PT_BOOL,
1146       .id       = "rewrite_sdt",
1147       .name     = N_("Rewrite SDT"),
1148       .desc     = N_("Rewrite SDT (Service Description Table) packets "
1149                      "to only include information about the currently-"
1150                      "streamed service."),
1151       .off      = offsetof(profile_mpegts_t, pro_rewrite_sdt),
1152       .opts     = PO_EXPERT,
1153       .def.i    = 1,
1154       .group    = 2
1155     },
1156     {
1157       .type     = PT_BOOL,
1158       .id       = "rewrite_eit",
1159       .name     = N_("Rewrite EIT"),
1160       .desc     = N_("Rewrite EIT (Event Information Table) packets "
1161                      "to only include information about the currently-"
1162                      "streamed service."),
1163       .off      = offsetof(profile_mpegts_t, pro_rewrite_eit),
1164       .opts     = PO_EXPERT,
1165       .def.i    = 1,
1166       .group    = 2
1167     },
1168     { }
1169   }
1170 };
1171 
1172 static int
profile_mpegts_pass_reopen(profile_chain_t * prch,muxer_config_t * m_cfg,int flags)1173 profile_mpegts_pass_reopen(profile_chain_t *prch,
1174                            muxer_config_t *m_cfg, int flags)
1175 {
1176   profile_mpegts_t *pro = (profile_mpegts_t *)prch->prch_pro;
1177   muxer_config_t c;
1178 
1179   if (m_cfg)
1180     c = *m_cfg; /* do not alter the original parameter */
1181   else
1182     memset(&c, 0, sizeof(c));
1183   if (c.m_type != MC_RAW)
1184     c.m_type = MC_PASS;
1185   c.m_rewrite_pat = pro->pro_rewrite_pat;
1186   c.m_rewrite_pmt = pro->pro_rewrite_pmt;
1187   c.m_rewrite_sdt = pro->pro_rewrite_sdt;
1188   c.m_rewrite_eit = pro->pro_rewrite_eit;
1189 
1190   assert(!prch->prch_muxer);
1191   prch->prch_muxer = muxer_create(&c);
1192   return 0;
1193 }
1194 
1195 static int
profile_mpegts_pass_open(profile_chain_t * prch,muxer_config_t * m_cfg,int flags,size_t qsize)1196 profile_mpegts_pass_open(profile_chain_t *prch,
1197                          muxer_config_t *m_cfg, int flags, size_t qsize)
1198 {
1199   prch->prch_flags = SUBSCRIPTION_MPEGTS;
1200 
1201   prch->prch_sq.sq_st.st_reject_filter = SMT_PACKET;
1202   prch->prch_sq.sq_maxsize = qsize;
1203 
1204   prch->prch_st    = &prch->prch_sq.sq_st;
1205 
1206   profile_mpegts_pass_reopen(prch, m_cfg, flags);
1207   return 0;
1208 }
1209 
1210 static profile_t *
profile_mpegts_pass_builder(void)1211 profile_mpegts_pass_builder(void)
1212 {
1213   profile_mpegts_t *pro = calloc(1, sizeof(*pro));
1214   pro->pro_sflags = SUBSCRIPTION_MPEGTS;
1215   pro->pro_reopen = profile_mpegts_pass_reopen;
1216   pro->pro_open   = profile_mpegts_pass_open;
1217   return (profile_t *)pro;
1218 }
1219 
1220 /*
1221  *  Matroska muxer
1222  */
1223 typedef struct profile_matroska {
1224   profile_t;
1225   int pro_webm;
1226 } profile_matroska_t;
1227 
1228 const idclass_t profile_matroska_class =
1229 {
1230   .ic_super      = &profile_class,
1231   .ic_class      = "profile-matroska",
1232   .ic_caption    = N_("Matroska (mkv)/built-in"),
1233   .ic_groups     = (const property_group_t[]) {
1234     {
1235       .name   = N_("Configuration"),
1236       .number = 1,
1237     },
1238     {
1239       .name   = N_("Matroska specific"),
1240       .number = 2,
1241     },
1242     {}
1243   },
1244   .ic_properties = (const property_t[]){
1245     {
1246       .type     = PT_BOOL,
1247       .id       = "webm",
1248       .name     = N_("WEBM"),
1249       .desc     = N_("Use WEBM format."),
1250       .off      = offsetof(profile_matroska_t, pro_webm),
1251       .opts     = PO_ADVANCED,
1252       .def.i    = 0,
1253       .group    = 2
1254     },
1255     { }
1256   }
1257 };
1258 
1259 static int
profile_matroska_reopen(profile_chain_t * prch,muxer_config_t * m_cfg,int flags)1260 profile_matroska_reopen(profile_chain_t *prch,
1261                         muxer_config_t *m_cfg, int flags)
1262 {
1263   profile_matroska_t *pro = (profile_matroska_t *)prch->prch_pro;
1264   muxer_config_t c;
1265 
1266   if (m_cfg)
1267     c = *m_cfg; /* do not alter the original parameter */
1268   else
1269     memset(&c, 0, sizeof(c));
1270   if (c.m_type != MC_WEBM)
1271     c.m_type = MC_MATROSKA;
1272   if (pro->pro_webm)
1273     c.m_type = MC_WEBM;
1274 
1275   assert(!prch->prch_muxer);
1276   prch->prch_muxer = muxer_create(&c);
1277   return 0;
1278 }
1279 
1280 static int
profile_matroska_open(profile_chain_t * prch,muxer_config_t * m_cfg,int flags,size_t qsize)1281 profile_matroska_open(profile_chain_t *prch,
1282                       muxer_config_t *m_cfg, int flags, size_t qsize)
1283 {
1284   streaming_target_t *dst;
1285 
1286   prch->prch_flags = SUBSCRIPTION_PACKET;
1287   prch->prch_sq.sq_maxsize = qsize;
1288 
1289   dst = prch->prch_gh    = globalheaders_create(&prch->prch_sq.sq_st);
1290   dst = prch->prch_tsfix = tsfix_create(dst);
1291   prch->prch_st    = dst;
1292 
1293   profile_matroska_reopen(prch, m_cfg, flags);
1294 
1295   return 0;
1296 }
1297 
1298 static profile_t *
profile_matroska_builder(void)1299 profile_matroska_builder(void)
1300 {
1301   profile_matroska_t *pro = calloc(1, sizeof(*pro));
1302   pro->pro_sflags = SUBSCRIPTION_PACKET;
1303   pro->pro_reopen = profile_matroska_reopen;
1304   pro->pro_open   = profile_matroska_open;
1305   return (profile_t *)pro;
1306 }
1307 
1308 
1309 /*
1310  *  Audioes Muxer
1311  */
1312 typedef struct profile_audio {
1313   profile_t;
1314   int pro_mc;
1315   int pro_index;
1316 } profile_audio_t;
1317 
1318 static htsmsg_t *
profile_class_mc_audio_list(void * o,const char * lang)1319 profile_class_mc_audio_list ( void *o, const char *lang )
1320 {
1321   static const struct strtab tab[] = {
1322     { N_("Any"),                          MC_UNKNOWN },
1323     { N_("MPEG-2 audio"),                 MC_MPEG2AUDIO, },
1324     { N_("AC3 audio"),                    MC_AC3, },
1325     { N_("AAC audio"),                    MC_AAC },
1326     { N_("MP4 audio"),                    MC_MP4A },
1327     { N_("Vorbis audio"),                 MC_VORBIS },
1328   };
1329   return strtab2htsmsg(tab, 1, lang);
1330 }
1331 
1332 const idclass_t profile_audio_class =
1333 {
1334   .ic_super      = &profile_class,
1335   .ic_class      = "profile-audio",
1336   .ic_caption    = N_("Audio stream"),
1337   .ic_properties = (const property_t[]){
1338     {
1339       .type     = PT_INT,
1340       .id       = "type",
1341       .name     = N_("Audio type"),
1342       .desc     = N_("Pick the stream with given audio type only."),
1343       .off      = offsetof(profile_audio_t, pro_mc),
1344       .list     = profile_class_mc_audio_list,
1345       .group    = 1
1346     },
1347     {
1348       .type     = PT_INT,
1349       .id       = "index",
1350       .name     = N_("Stream index"),
1351       .desc     = N_("Stream index (starts with zero)."),
1352       .off      = offsetof(profile_audio_t, pro_index),
1353       .group    = 1
1354     },
1355     { }
1356   }
1357 };
1358 
1359 
1360 static int
profile_audio_reopen(profile_chain_t * prch,muxer_config_t * m_cfg,int flags)1361 profile_audio_reopen(profile_chain_t *prch,
1362                      muxer_config_t *m_cfg, int flags)
1363 {
1364   muxer_config_t c;
1365   profile_audio_t *pro = (profile_audio_t *)prch->prch_pro;
1366 
1367   if (m_cfg)
1368     c = *m_cfg; /* do not alter the original parameter */
1369   else
1370     memset(&c, 0, sizeof(c));
1371   c.m_type = pro->pro_mc != MC_UNKNOWN ? pro->pro_mc : MC_MPEG2AUDIO;
1372   c.m_force_type = pro->pro_mc;
1373   c.m_index = pro->pro_index;
1374 
1375   assert(!prch->prch_muxer);
1376   prch->prch_muxer = muxer_create(&c);
1377   return 0;
1378 }
1379 
1380 static int
profile_audio_open(profile_chain_t * prch,muxer_config_t * m_cfg,int flags,size_t qsize)1381 profile_audio_open(profile_chain_t *prch,
1382                    muxer_config_t *m_cfg, int flags, size_t qsize)
1383 {
1384   int r;
1385 
1386   prch->prch_flags = SUBSCRIPTION_PACKET;
1387   prch->prch_sq.sq_maxsize = qsize;
1388 
1389   r = profile_htsp_work(prch, &prch->prch_sq.sq_st, 0, 0);
1390   if (r) {
1391     profile_chain_close(prch);
1392     return r;
1393   }
1394 
1395   profile_audio_reopen(prch, m_cfg, flags);
1396   return 0;
1397 }
1398 
1399 static profile_t *
profile_audio_builder(void)1400 profile_audio_builder(void)
1401 {
1402   profile_audio_t *pro = calloc(1, sizeof(*pro));
1403   pro->pro_sflags = SUBSCRIPTION_PACKET;
1404   pro->pro_reopen = profile_audio_reopen;
1405   pro->pro_open   = profile_audio_open;
1406   return (profile_t *)pro;
1407 }
1408 
1409 
1410 #if ENABLE_LIBAV
1411 
1412 /*
1413  *  LibAV/MPEG-TS muxer
1414  */
1415 typedef struct profile_libav_mpegts {
1416   profile_t;
1417 } profile_libav_mpegts_t;
1418 
1419 const idclass_t profile_libav_mpegts_class =
1420 {
1421   .ic_super      = &profile_class,
1422   .ic_class      = "profile-libav-mpegts",
1423   .ic_caption    = N_("MPEG-TS/av-lib"),
1424   .ic_properties = (const property_t[]){
1425     { }
1426   }
1427 };
1428 
1429 static int
profile_libav_mpegts_reopen(profile_chain_t * prch,muxer_config_t * m_cfg,int flags)1430 profile_libav_mpegts_reopen(profile_chain_t *prch,
1431                             muxer_config_t *m_cfg, int flags)
1432 {
1433   muxer_config_t c;
1434 
1435   if (m_cfg)
1436     c = *m_cfg; /* do not alter the original parameter */
1437   else
1438     memset(&c, 0, sizeof(c));
1439   c.m_type = MC_MPEGTS;
1440 
1441   assert(!prch->prch_muxer);
1442   prch->prch_muxer = muxer_create(&c);
1443   return 0;
1444 }
1445 
1446 static int
profile_libav_mpegts_open(profile_chain_t * prch,muxer_config_t * m_cfg,int flags,size_t qsize)1447 profile_libav_mpegts_open(profile_chain_t *prch,
1448                           muxer_config_t *m_cfg, int flags, size_t qsize)
1449 {
1450   int r;
1451 
1452   prch->prch_flags = SUBSCRIPTION_PACKET;
1453   prch->prch_sq.sq_maxsize = qsize;
1454 
1455   r = profile_htsp_work(prch, &prch->prch_sq.sq_st, 0, 0);
1456   if (r) {
1457     profile_chain_close(prch);
1458     return r;
1459   }
1460 
1461   profile_libav_mpegts_reopen(prch, m_cfg, flags);
1462   return 0;
1463 }
1464 
1465 static profile_t *
profile_libav_mpegts_builder(void)1466 profile_libav_mpegts_builder(void)
1467 {
1468   profile_libav_mpegts_t *pro = calloc(1, sizeof(*pro));
1469   pro->pro_sflags = SUBSCRIPTION_PACKET;
1470   pro->pro_reopen = profile_libav_mpegts_reopen;
1471   pro->pro_open   = profile_libav_mpegts_open;
1472   return (profile_t *)pro;
1473 }
1474 
1475 /*
1476  *  LibAV/Matroska muxer
1477  */
1478 typedef struct profile_libav_matroska {
1479   profile_t;
1480   int pro_webm;
1481 } profile_libav_matroska_t;
1482 
1483 const idclass_t profile_libav_matroska_class =
1484 {
1485   .ic_super      = &profile_class,
1486   .ic_class      = "profile-libav-matroska",
1487   .ic_caption    = N_("Matroska/av-lib"),
1488   .ic_groups     = (const property_group_t[]) {
1489     {
1490       .name   = N_("Configuration"),
1491       .number = 1,
1492     },
1493     {
1494       .name   = N_("Matroska specific"),
1495       .number = 2,
1496     },
1497     {}
1498   },
1499   .ic_properties = (const property_t[]){
1500     {
1501       .type     = PT_BOOL,
1502       .id       = "webm",
1503       .name     = N_("WEBM"),
1504       .desc     = N_("Use WEBM format."),
1505       .off      = offsetof(profile_libav_matroska_t, pro_webm),
1506       .opts     = PO_ADVANCED,
1507       .def.i    = 0,
1508       .group    = 2
1509     },
1510     { }
1511   }
1512 };
1513 
1514 static int
profile_libav_matroska_reopen(profile_chain_t * prch,muxer_config_t * m_cfg,int flags)1515 profile_libav_matroska_reopen(profile_chain_t *prch,
1516                               muxer_config_t *m_cfg, int flags)
1517 {
1518   profile_libav_matroska_t *pro = (profile_libav_matroska_t *)prch->prch_pro;
1519   muxer_config_t c;
1520 
1521   if (m_cfg)
1522     c = *m_cfg; /* do not alter the original parameter */
1523   else
1524     memset(&c, 0, sizeof(c));
1525   if (c.m_type != MC_AVWEBM)
1526     c.m_type = MC_AVMATROSKA;
1527   if (pro->pro_webm)
1528     c.m_type = MC_AVWEBM;
1529 
1530   assert(!prch->prch_muxer);
1531   prch->prch_muxer = muxer_create(&c);
1532   return 0;
1533 }
1534 
1535 static int
profile_libav_matroska_open(profile_chain_t * prch,muxer_config_t * m_cfg,int flags,size_t qsize)1536 profile_libav_matroska_open(profile_chain_t *prch,
1537                             muxer_config_t *m_cfg, int flags, size_t qsize)
1538 {
1539   int r;
1540 
1541   prch->prch_flags = SUBSCRIPTION_PACKET;
1542   prch->prch_sq.sq_maxsize = qsize;
1543 
1544   r = profile_htsp_work(prch, &prch->prch_sq.sq_st, 0, 0);
1545   if (r) {
1546     profile_chain_close(prch);
1547     return r;
1548   }
1549 
1550   profile_libav_matroska_reopen(prch, m_cfg, flags);
1551 
1552   return 0;
1553 }
1554 
1555 static profile_t *
profile_libav_matroska_builder(void)1556 profile_libav_matroska_builder(void)
1557 {
1558   profile_libav_matroska_t *pro = calloc(1, sizeof(*pro));
1559   pro->pro_sflags = SUBSCRIPTION_PACKET;
1560   pro->pro_reopen = profile_libav_matroska_reopen;
1561   pro->pro_open   = profile_libav_matroska_open;
1562   return (profile_t *)pro;
1563 }
1564 
1565 /*
1566  *  LibAV/MP4 muxer
1567  */
1568 typedef struct profile_libav_mp4 {
1569   profile_t;
1570 } profile_libav_mp4_t;
1571 
1572 const idclass_t profile_libav_mp4_class =
1573 {
1574   .ic_super      = &profile_class,
1575   .ic_class      = "profile-libav-mp4",
1576   .ic_caption    = N_("MP4/av-lib"),
1577 };
1578 
1579 static int
profile_libav_mp4_reopen(profile_chain_t * prch,muxer_config_t * m_cfg,int flags)1580 profile_libav_mp4_reopen(profile_chain_t *prch,
1581                          muxer_config_t *m_cfg, int flags)
1582 {
1583   muxer_config_t c;
1584 
1585   if (m_cfg)
1586     c = *m_cfg; /* do not alter the original parameter */
1587   else
1588     memset(&c, 0, sizeof(c));
1589   if (c.m_type != MC_AVMP4)
1590     c.m_type = MC_AVMP4;
1591 
1592   assert(!prch->prch_muxer);
1593   prch->prch_muxer = muxer_create(&c);
1594   return 0;
1595 }
1596 
1597 static int
profile_libav_mp4_open(profile_chain_t * prch,muxer_config_t * m_cfg,int flags,size_t qsize)1598 profile_libav_mp4_open(profile_chain_t *prch,
1599                        muxer_config_t *m_cfg, int flags, size_t qsize)
1600 {
1601   int r;
1602 
1603   prch->prch_flags = SUBSCRIPTION_PACKET;
1604   prch->prch_sq.sq_maxsize = qsize;
1605 
1606   r = profile_htsp_work(prch, &prch->prch_sq.sq_st, 0, 0);
1607   if (r) {
1608     profile_chain_close(prch);
1609     return r;
1610   }
1611 
1612   profile_libav_mp4_reopen(prch, m_cfg, flags);
1613 
1614   return 0;
1615 }
1616 
1617 static profile_t *
profile_libav_mp4_builder(void)1618 profile_libav_mp4_builder(void)
1619 {
1620   profile_libav_mp4_t *pro = calloc(1, sizeof(*pro));
1621   pro->pro_sflags = SUBSCRIPTION_PACKET;
1622   pro->pro_reopen = profile_libav_mp4_reopen;
1623   pro->pro_open   = profile_libav_mp4_open;
1624   return (profile_t *)pro;
1625 }
1626 
1627 /*
1628  *  Transcoding + packet-like muxers
1629  */
1630 
1631 static int profile_transcode_experimental_codecs = 1;
1632 
1633 typedef struct profile_transcode {
1634   profile_t;
1635   int      pro_mc;
1636   uint32_t pro_resolution;
1637   uint32_t pro_channels;
1638   uint32_t pro_vbitrate;
1639   uint32_t pro_abitrate;
1640   char    *pro_language;
1641   char    *pro_vcodec;
1642   char    *pro_vcodec_preset;
1643   char    *pro_acodec;
1644   char    *pro_scodec;
1645   char    *pro_src_vcodec;
1646 } profile_transcode_t;
1647 
1648 
1649 static htsmsg_t *
profile_class_src_vcodec_list(void * o,const char * lang)1650 profile_class_src_vcodec_list ( void *o, const char *lang )
1651 {
1652   static const struct strtab_str tab[] = {
1653     { N_("Any"),		"" },
1654     { "MPEG2VIDEO",      	"MPEG2VIDEO" },
1655     { "H264",      		"H264" },
1656     { "VP8",      		"VP8" },
1657     { "HEVC",      		"HEVC" },
1658     { "VP9",      		"VP9" },
1659   };
1660   return strtab2htsmsg_str(tab, 1, lang);
1661 }
1662 
1663 static htsmsg_t *
profile_class_mc_list(void * o,const char * lang)1664 profile_class_mc_list ( void *o, const char *lang )
1665 {
1666   static const struct strtab tab[] = {
1667     { N_("Not set"),                      MC_UNKNOWN },
1668     { N_("Matroska (mkv)/built-in"),      MC_MATROSKA, },
1669     { N_("WEBM/built-in"),                MC_WEBM, },
1670     { N_("MPEG-TS/av-lib"),               MC_MPEGTS },
1671     { N_("MPEG-PS (DVD)/av-lib"),         MC_MPEGPS },
1672     { N_("Raw Audio Stream"),             MC_MPEG2AUDIO },
1673     { N_("Matroska (mkv)/av-lib"),        MC_AVMATROSKA },
1674     { N_("WEBM/av-lib"),                  MC_AVWEBM },
1675     { N_("MP4/av-lib"),                   MC_AVMP4 },
1676   };
1677   return strtab2htsmsg(tab, 1, lang);
1678 }
1679 
1680 static htsmsg_t *
profile_class_channels_list(void * o,const char * lang)1681 profile_class_channels_list ( void *o, const char *lang )
1682 {
1683   static const struct strtab tab[] = {
1684     { N_("Copy layout"),                   0 },
1685     { N_("Mono"),                          1 },
1686     { N_("Stereo"),                        2 },
1687     { N_("Surround (2 front, rear mono)"), 3 },
1688     { N_("Quad (4.0)"),                    4 },
1689     { N_("5.0"),                           5 },
1690     { N_("5.1"),                           6 },
1691     { N_("6.1"),                           7 },
1692     { N_("7.1"),                           8 }
1693   };
1694   return strtab2htsmsg(tab, 1, lang);
1695 }
1696 
1697 static htsmsg_t *
profile_class_language_list(void * o,const char * lang)1698 profile_class_language_list(void *o, const char *lang)
1699 {
1700   htsmsg_t *l = htsmsg_create_list();
1701   const lang_code_t *lc = lang_codes;
1702   char buf[128];
1703 
1704   while (lc->code2b) {
1705     htsmsg_t *e;
1706     if (!strcmp(lc->code2b, "und")) {
1707       e = htsmsg_create_key_val("", tvh_gettext_lang(lang, N_("Use original")));
1708     } else {
1709       snprintf(buf, sizeof(buf), "%s (%s)", lc->desc, lc->code2b);
1710       buf[sizeof(buf)-1] = '\0';
1711       e = htsmsg_create_key_val(lc->code2b, buf);
1712     }
1713     htsmsg_add_msg(l, NULL, e);
1714     lc++;
1715   }
1716   return l;
1717 }
1718 
1719 static inline int
profile_class_check_sct(htsmsg_t * c,int sct)1720 profile_class_check_sct(htsmsg_t *c, int sct)
1721 {
1722   htsmsg_field_t *f;
1723   int64_t x;
1724   HTSMSG_FOREACH(f, c)
1725     if (!htsmsg_field_get_s64(f, &x))
1726       if (x == sct)
1727         return 1;
1728   return 0;
1729 }
1730 
1731 static htsmsg_t *
profile_class_codec_list(int (* check)(int sct),const char * lang)1732 profile_class_codec_list(int (*check)(int sct), const char *lang)
1733 {
1734   htsmsg_t *l = htsmsg_create_list(), *e, *c, *m;
1735   htsmsg_field_t *f;
1736   const char *s, *s2;
1737   char buf[128];
1738   int sct;
1739 
1740   e = htsmsg_create_key_val("", tvh_gettext_lang(lang, N_("Do not use")));
1741   htsmsg_add_msg(l, NULL, e);
1742   e = htsmsg_create_key_val("copy", tvh_gettext_lang(lang, N_("Copy codec type")));
1743   htsmsg_add_msg(l, NULL, e);
1744   c = transcoder_get_capabilities(profile_transcode_experimental_codecs);
1745   HTSMSG_FOREACH(f, c) {
1746     if (!(m = htsmsg_field_get_map(f)))
1747       continue;
1748     if (htsmsg_get_s32(m, "type", &sct))
1749       continue;
1750     if (!check(sct))
1751       continue;
1752     if (!(s = htsmsg_get_str(m, "name")))
1753       continue;
1754     s2 = htsmsg_get_str(m, "long_name");
1755     if (s2)
1756       snprintf(buf, sizeof(buf), "%s: %s", s, s2);
1757     else
1758       snprintf(buf, sizeof(buf), "%s", s);
1759     e = htsmsg_create_key_val(s, buf);
1760     htsmsg_add_msg(l, NULL, e);
1761   }
1762   htsmsg_destroy(c);
1763   return l;
1764 }
1765 
1766 static int
profile_class_vcodec_sct_check(int sct)1767 profile_class_vcodec_sct_check(int sct)
1768 {
1769   return SCT_ISVIDEO(sct);
1770 }
1771 
1772 static htsmsg_t *
profile_class_vcodec_list(void * o,const char * lang)1773 profile_class_vcodec_list(void *o, const char *lang)
1774 {
1775   return profile_class_codec_list(profile_class_vcodec_sct_check, lang);
1776 }
1777 
1778 static htsmsg_t *
profile_class_vcodec_preset_list(void * o,const char * lang)1779 profile_class_vcodec_preset_list(void *o, const char *lang)
1780 {
1781   static const struct strtab_str tab[] = {
1782     {N_("ultrafast: h264 / h265") 	     , "ultrafast" },
1783     {N_("superfast: h264 / h265") 	     , "superfast" },
1784     {N_("veryfast: h264 / h265 / qsv(h264)") 	     , "veryfast"  },
1785     {N_("faster: h264 / h265 / qsv(h264)") 	     , "faster"    },
1786     {N_("fast: h264 / h265 / qsv(h264 / h265)") 	     , "fast"      },
1787     {N_("medium: h264 / h265 / qsv(h264 / h265)") 	     , "medium"    },
1788     {N_("slow: h264 / h265 / qsv(h264 / h265)") 	     , "slow"      },
1789     {N_("slower: h264 / h265 / qsv(h264)") 	     , "slower"    },
1790     {N_("veryslow: h264 / h265 / qsv(h264)") 	     , "veryslow"  },
1791     {N_("placebo: h264 / h265") 	     , "placebo"   },
1792     {N_("hq: nvenc(h264 / h265)") 	     , "hq"        },
1793     {N_("hp: nvenc(h264 / h265)") 	     , "hp"        },
1794     {N_("bd: nvenc(h264 / h265)") 	     , "bd"        },
1795     {N_("ll: nvenc(h264 / h265)") 	     , "ll"        },
1796     {N_("llhq: nvenc(h264 / h265)")     , "llhq"      },
1797     {N_("llhp: nvenc(h264 / h265)")     , "llhp"      },
1798     {N_("default: nvenc(h264 / h265)")  , "default"   }
1799   };
1800   return strtab2htsmsg_str(tab, 1, lang);
1801 }
1802 
1803 static int
profile_class_acodec_sct_check(int sct)1804 profile_class_acodec_sct_check(int sct)
1805 {
1806   return SCT_ISAUDIO(sct);
1807 }
1808 
1809 static htsmsg_t *
profile_class_acodec_list(void * o,const char * lang)1810 profile_class_acodec_list(void *o, const char *lang)
1811 {
1812   return profile_class_codec_list(profile_class_acodec_sct_check, lang);
1813 }
1814 
1815 static int
profile_class_scodec_sct_check(int sct)1816 profile_class_scodec_sct_check(int sct)
1817 {
1818   return SCT_ISSUBTITLE(sct);
1819 }
1820 
1821 static htsmsg_t *
profile_class_scodec_list(void * o,const char * lang)1822 profile_class_scodec_list(void *o, const char *lang)
1823 {
1824   return profile_class_codec_list(profile_class_scodec_sct_check, lang);
1825 }
1826 
1827 const idclass_t profile_transcode_class =
1828 {
1829   .ic_super      = &profile_class,
1830   .ic_class      = "profile-transcode",
1831   .ic_caption    = N_("Transcode/av-lib"),
1832   .ic_groups     = (const property_group_t[]) {
1833     {
1834       .name   = N_("Configuration"),
1835       .number = 1,
1836     },
1837     {
1838       .name   = N_("Transcoding"),
1839       .number = 2,
1840     },
1841     {}
1842   },
1843   .ic_properties = (const property_t[]){
1844     {
1845       .type     = PT_INT,
1846       .id       = "container",
1847       .name     = N_("Container"),
1848       .desc     = N_("Container to use for the transcoded stream."),
1849       .off      = offsetof(profile_transcode_t, pro_mc),
1850       .def.i    = MC_MATROSKA,
1851       .list     = profile_class_mc_list,
1852       .group    = 1
1853     },
1854     {
1855       .type     = PT_U32,
1856       .id       = "resolution",
1857       .name     = N_("Resolution (height)"),
1858       .desc     = N_("Vertical resolution (height) of the output video "
1859                      "stream. Horizontal resolution is adjusted "
1860                      "automatically to preserve aspect ratio. When set "
1861                      "to 0, the input resolution is used."),
1862       .off      = offsetof(profile_transcode_t, pro_resolution),
1863       .def.u32  = 384,
1864       .group    = 2
1865     },
1866     {
1867       .type     = PT_U32,
1868       .id       = "channels",
1869       .name     = N_("Channels"),
1870       .desc     = N_("Audio channel layout."),
1871       .off      = offsetof(profile_transcode_t, pro_channels),
1872       .def.u32  = 2,
1873       .list     = profile_class_channels_list,
1874       .opts     = PO_ADVANCED,
1875       .group    = 2
1876     },
1877     {
1878       .type     = PT_STR,
1879       .id       = "language",
1880       .name     = N_("Language"),
1881       .desc     = N_("Preferred audio language."),
1882       .off      = offsetof(profile_transcode_t, pro_language),
1883       .list     = profile_class_language_list,
1884       .opts     = PO_ADVANCED,
1885       .group    = 2
1886     },
1887     {
1888       .type     = PT_STR,
1889       .id       = "src_vcodec",
1890       .name     = N_("Source video codec"),
1891       .desc     = N_("Transcode video only if source video codec matches. "
1892                      "\"Any\" ignores source video codec checking and "
1893                      "always transcodes. If no codec match is found, "
1894                      "transcoding is done using the \"copy\" codec. "
1895                      "if a match is found, transcode with the "
1896                      "parameters in this profile. Separate codec names "
1897                      "with comma."),
1898       .off      = offsetof(profile_transcode_t, pro_src_vcodec),
1899       .def.i    = SCT_UNKNOWN,
1900       .list     = profile_class_src_vcodec_list,
1901       .opts     = PO_ADVANCED,
1902       .group    = 2
1903     },
1904     {
1905       .type     = PT_STR,
1906       .id       = "vcodec",
1907       .name     = N_("Video codec"),
1908       .desc     = N_("Video codec to use for the transcode. "
1909                      "\"Do not use\" will disable video output."),
1910       .off      = offsetof(profile_transcode_t, pro_vcodec),
1911       .def.s    = "libx264",
1912       .list     = profile_class_vcodec_list,
1913       .opts     = PO_ADVANCED,
1914       .group    = 2
1915     },
1916     {
1917       .type     = PT_STR,
1918       .id       = "vcodec_preset",
1919       .name     = N_("Video codec preset"),
1920       .desc     = N_("Video codec preset to use for transcoding."),
1921       .off      = offsetof(profile_transcode_t, pro_vcodec_preset),
1922       .def.s    = "faster",
1923       .list     = profile_class_vcodec_preset_list,
1924       .opts     = PO_ADVANCED,
1925       .group    = 2
1926     },
1927     {
1928       .type     = PT_U32,
1929       .id       = "vbitrate",
1930       .name     = N_("Video bitrate (kb/s) (0=auto)"),
1931       .desc     = N_("Bitrate to use for the transcode. See Help for "
1932                      "details."),
1933       .off      = offsetof(profile_transcode_t, pro_vbitrate),
1934       .opts     = PO_ADVANCED,
1935       .def.u32  = 0,
1936       .group    = 2
1937     },
1938     {
1939       .type     = PT_STR,
1940       .id       = "acodec",
1941       .name     = N_("Audio codec"),
1942       .desc     = N_("Audio codec to use for the transcode. \"Do not "
1943                      "use\" will disable audio output."),
1944       .off      = offsetof(profile_transcode_t, pro_acodec),
1945       .def.s    = "libvorbis",
1946       .list     = profile_class_acodec_list,
1947       .opts     = PO_ADVANCED,
1948       .group    = 2
1949     },
1950     {
1951       .type     = PT_U32,
1952       .id       = "abitrate",
1953       .name     = N_("Audio bitrate (kb/s) (0=auto)"),
1954       .desc     = N_("Audio bitrate to use for transcoding."),
1955       .off      = offsetof(profile_transcode_t, pro_abitrate),
1956       .opts     = PO_ADVANCED,
1957       .def.u32  = 0,
1958       .group    = 2
1959     },
1960     {
1961       .type     = PT_STR,
1962       .id       = "scodec",
1963       .name     = N_("Subtitle codec"),
1964       .desc     = N_("Select subtitle codec to use for transcoding."),
1965       .off      = offsetof(profile_transcode_t, pro_scodec),
1966       .def.s    = "",
1967       .list     = profile_class_scodec_list,
1968       .opts     = PO_ADVANCED,
1969       .group    = 2
1970     },
1971     { }
1972   }
1973 };
1974 
1975 static int
profile_transcode_resolution(profile_transcode_t * pro)1976 profile_transcode_resolution(profile_transcode_t *pro)
1977 {
1978   return pro->pro_resolution == 0 ? 0 :
1979          (pro->pro_resolution >= 240 ? pro->pro_resolution : 240);
1980 }
1981 
1982 static int
profile_transcode_vbitrate(profile_transcode_t * pro)1983 profile_transcode_vbitrate(profile_transcode_t *pro)
1984 {
1985   return pro->pro_vbitrate;
1986 }
1987 
1988 static int
profile_transcode_abitrate(profile_transcode_t * pro)1989 profile_transcode_abitrate(profile_transcode_t *pro)
1990 {
1991   return pro->pro_abitrate;
1992 }
1993 
1994 static int
profile_transcode_can_share(profile_chain_t * prch,profile_chain_t * joiner)1995 profile_transcode_can_share(profile_chain_t *prch,
1996                             profile_chain_t *joiner)
1997 {
1998   profile_transcode_t *pro1 = (profile_transcode_t *)prch->prch_pro;
1999   profile_transcode_t *pro2 = (profile_transcode_t *)joiner->prch_pro;
2000   if (pro1 == pro2)
2001     return 1;
2002   if (!idnode_is_instance(&pro2->pro_id, &profile_transcode_class))
2003     return 0;
2004   /*
2005    * Do full params check here, note that profiles might differ
2006    * only in the muxer setup.
2007    */
2008   if (strcmp(pro1->pro_vcodec ?: "", pro2->pro_vcodec ?: ""))
2009     return 0;
2010   if (strcmp(pro1->pro_vcodec_preset ?: "", pro2->pro_vcodec_preset ?: ""))
2011     return 0;
2012   if (strcmp(pro1->pro_acodec ?: "", pro2->pro_acodec ?: ""))
2013     return 0;
2014   if (strcmp(pro1->pro_scodec ?: "", pro2->pro_scodec ?: ""))
2015     return 0;
2016   if (profile_transcode_resolution(pro1) != profile_transcode_resolution(pro2))
2017     return 0;
2018   if (profile_transcode_vbitrate(pro1) != profile_transcode_vbitrate(pro2))
2019     return 0;
2020   if (profile_transcode_abitrate(pro1) != profile_transcode_abitrate(pro2))
2021     return 0;
2022   if (strcmp(pro1->pro_language ?: "", pro2->pro_language ?: ""))
2023     return 0;
2024   return 1;
2025 }
2026 
2027 static int
profile_transcode_work(profile_chain_t * prch,streaming_target_t * dst,uint32_t timeshift_period,int flags)2028 profile_transcode_work(profile_chain_t *prch,
2029                        streaming_target_t *dst,
2030                        uint32_t timeshift_period, int flags)
2031 {
2032   profile_sharer_t *prsh;
2033   profile_transcode_t *pro = (profile_transcode_t *)prch->prch_pro;
2034   transcoder_props_t props;
2035 
2036   prsh = profile_sharer_find(prch);
2037   if (!prsh)
2038     goto fail;
2039 
2040   prch->prch_can_share = profile_transcode_can_share;
2041 
2042   memset(&props, 0, sizeof(props));
2043   strncpy(props.tp_vcodec, pro->pro_vcodec ?: "", sizeof(props.tp_vcodec)-1);
2044   strncpy(props.tp_vcodec_preset, pro->pro_vcodec_preset ?: "", sizeof(props.tp_vcodec_preset)-1);
2045   strncpy(props.tp_acodec, pro->pro_acodec ?: "", sizeof(props.tp_acodec)-1);
2046   strncpy(props.tp_scodec, pro->pro_scodec ?: "", sizeof(props.tp_scodec)-1);
2047   props.tp_resolution = profile_transcode_resolution(pro);
2048   props.tp_channels   = pro->pro_channels;
2049   props.tp_vbitrate   = profile_transcode_vbitrate(pro);
2050   props.tp_abitrate   = profile_transcode_abitrate(pro);
2051   strncpy(props.tp_language, pro->pro_language ?: "", 3);
2052 
2053   if (!pro->pro_src_vcodec) {
2054      strcpy(props.tp_src_vcodec, "");
2055   } else if(!strncasecmp("Any",pro->pro_src_vcodec,3)) {
2056      strcpy(props.tp_src_vcodec, "");
2057   } else {
2058      strncpy(props.tp_src_vcodec, pro->pro_src_vcodec ?: "", sizeof(props.tp_src_vcodec)-1);
2059   }
2060 
2061   dst = prch->prch_gh = globalheaders_create(dst);
2062 
2063 #if ENABLE_TIMESHIFT
2064   if (timeshift_period > 0)
2065     dst = prch->prch_timeshift = timeshift_create(dst, timeshift_period);
2066 #endif
2067   if (profile_sharer_create(prsh, prch, dst))
2068     goto fail;
2069   if (!prsh->prsh_transcoder) {
2070     assert(!prsh->prsh_tsfix);
2071     dst = prsh->prsh_transcoder = transcoder_create(&prsh->prsh_input);
2072     if (!dst)
2073       goto fail;
2074     transcoder_set_properties(dst, &props);
2075     prsh->prsh_tsfix = tsfix_create(dst);
2076   }
2077   prch->prch_share = prsh->prsh_tsfix;
2078   streaming_target_init(&prch->prch_input, &profile_input_ops, prch, 0);
2079   prch->prch_st = &prch->prch_input;
2080   return 0;
2081 fail:
2082   profile_chain_close(prch);
2083   return -1;
2084 }
2085 
2086 static int
profile_transcode_mc_valid(int mc)2087 profile_transcode_mc_valid(int mc)
2088 {
2089   switch (mc) {
2090   case MC_MATROSKA:
2091   case MC_WEBM:
2092   case MC_MPEGTS:
2093   case MC_MPEGPS:
2094   case MC_MPEG2AUDIO:
2095   case MC_AC3:
2096   case MC_AAC:
2097   case MC_VORBIS:
2098   case MC_AVMATROSKA:
2099   case MC_AVMP4:
2100     return 1;
2101   default:
2102     return 0;
2103   }
2104 }
2105 
2106 static int
profile_transcode_reopen(profile_chain_t * prch,muxer_config_t * m_cfg,int flags)2107 profile_transcode_reopen(profile_chain_t *prch,
2108                          muxer_config_t *m_cfg, int flags)
2109 {
2110   profile_transcode_t *pro = (profile_transcode_t *)prch->prch_pro;
2111   muxer_config_t c;
2112 
2113   if (m_cfg)
2114     c = *m_cfg; /* do not alter the original parameter */
2115   else
2116     memset(&c, 0, sizeof(c));
2117   if (!profile_transcode_mc_valid(c.m_type)) {
2118     c.m_type = pro->pro_mc;
2119     if (!profile_transcode_mc_valid(c.m_type))
2120       c.m_type = MC_MATROSKA;
2121   }
2122 
2123   assert(!prch->prch_muxer);
2124   prch->prch_muxer = muxer_create(&c);
2125   return 0;
2126 }
2127 
2128 static int
profile_transcode_open(profile_chain_t * prch,muxer_config_t * m_cfg,int flags,size_t qsize)2129 profile_transcode_open(profile_chain_t *prch,
2130                        muxer_config_t *m_cfg, int flags, size_t qsize)
2131 {
2132   int r;
2133 
2134   prch->prch_flags = SUBSCRIPTION_PACKET;
2135   prch->prch_sq.sq_maxsize = qsize;
2136 
2137   r = profile_transcode_work(prch, &prch->prch_sq.sq_st, 0, 0);
2138   if (r) {
2139     profile_chain_close(prch);
2140     return r;
2141   }
2142 
2143   profile_transcode_reopen(prch, m_cfg, flags);
2144   return 0;
2145 }
2146 
2147 static void
profile_transcode_free(profile_t * _pro)2148 profile_transcode_free(profile_t *_pro)
2149 {
2150   profile_transcode_t *pro = (profile_transcode_t *)_pro;
2151   free(pro->pro_vcodec);
2152   free(pro->pro_vcodec_preset);
2153   free(pro->pro_acodec);
2154   free(pro->pro_scodec);
2155   free(pro->pro_src_vcodec);
2156 }
2157 
2158 static profile_t *
profile_transcode_builder(void)2159 profile_transcode_builder(void)
2160 {
2161   profile_transcode_t *pro = calloc(1, sizeof(*pro));
2162   pro->pro_sflags = SUBSCRIPTION_PACKET;
2163   pro->pro_free   = profile_transcode_free;
2164   pro->pro_work   = profile_transcode_work;
2165   pro->pro_reopen = profile_transcode_reopen;
2166   pro->pro_open   = profile_transcode_open;
2167   return (profile_t *)pro;
2168 }
2169 
2170 #endif /* ENABLE_TRANSCODE */
2171 
2172 /*
2173  *  Initialize
2174  */
2175 void
profile_init(void)2176 profile_init(void)
2177 {
2178   htsmsg_t *c, *e;
2179   htsmsg_field_t *f;
2180   profile_t *pro;
2181   const char *name;
2182 
2183   LIST_INIT(&profile_builders);
2184   TAILQ_INIT(&profiles);
2185   LIST_INIT(&profile_chains);
2186 
2187   profile_register(&profile_mpegts_pass_class, profile_mpegts_pass_builder);
2188   profile_register(&profile_matroska_class, profile_matroska_builder);
2189   profile_register(&profile_htsp_class, profile_htsp_builder);
2190   profile_register(&profile_audio_class, profile_audio_builder);
2191 #if ENABLE_LIBAV
2192   profile_register(&profile_libav_mpegts_class, profile_libav_mpegts_builder);
2193   profile_register(&profile_libav_matroska_class, profile_libav_matroska_builder);
2194   profile_register(&profile_libav_mp4_class, profile_libav_mp4_builder);
2195   profile_transcode_experimental_codecs =
2196     getenv("TVHEADEND_LIBAV_NO_EXPERIMENTAL_CODECS") ? 0 : 1;
2197   profile_register(&profile_transcode_class, profile_transcode_builder);
2198 #endif
2199 
2200   if ((c = hts_settings_load("profile")) != NULL) {
2201     HTSMSG_FOREACH(f, c) {
2202       if (!(e = htsmsg_field_get_map(f)))
2203         continue;
2204       (void)profile_create(f->hmf_name, e, 0);
2205     }
2206     htsmsg_destroy(c);
2207   }
2208 
2209   name = "pass";
2210   pro = profile_find_by_name2(name, NULL, 1);
2211   if (pro == NULL || strcmp(profile_get_name(pro), name)) {
2212     htsmsg_t *conf;
2213 
2214     conf = htsmsg_create_map();
2215     htsmsg_add_str (conf, "class", "profile-mpegts");
2216     htsmsg_add_bool(conf, "enabled", 1);
2217     htsmsg_add_bool(conf, "default", 1);
2218     htsmsg_add_str (conf, "name", name);
2219     htsmsg_add_str (conf, "comment", _("MPEG-TS Pass-thru"));
2220     htsmsg_add_s32 (conf, "priority", PROFILE_SPRIO_NORMAL);
2221     htsmsg_add_bool(conf, "rewrite_pmt", 1);
2222     htsmsg_add_bool(conf, "rewrite_pat", 1);
2223     htsmsg_add_bool(conf, "rewrite_sdt", 1);
2224     htsmsg_add_bool(conf, "rewrite_eit", 1);
2225     htsmsg_add_bool(conf, "shield", 1);
2226     (void)profile_create(NULL, conf, 1);
2227     htsmsg_destroy(conf);
2228   }
2229 
2230   name = "matroska";
2231   pro = profile_find_by_name2(name, NULL, 1);
2232   if (pro == NULL || strcmp(profile_get_name(pro), name)) {
2233     htsmsg_t *conf;
2234 
2235     conf = htsmsg_create_map();
2236     htsmsg_add_str (conf, "class", "profile-matroska");
2237     htsmsg_add_bool(conf, "enabled", 1);
2238     htsmsg_add_str (conf, "name", name);
2239     htsmsg_add_str (conf, "comment", _("Matroska"));
2240     htsmsg_add_s32 (conf, "priority", PROFILE_SPRIO_NORMAL);
2241     htsmsg_add_bool(conf, "shield", 1);
2242     (void)profile_create(NULL, conf, 1);
2243     htsmsg_destroy(conf);
2244   }
2245 
2246   name = "htsp";
2247   pro = profile_find_by_name2(name, NULL, 1);
2248   if (pro == NULL || strcmp(profile_get_name(pro), name)) {
2249     htsmsg_t *conf;
2250 
2251     conf = htsmsg_create_map();
2252     htsmsg_add_str (conf, "class", "profile-htsp");
2253     htsmsg_add_bool(conf, "enabled", 1);
2254     htsmsg_add_str (conf, "name", name);
2255     htsmsg_add_str (conf, "comment", _("HTSP Default Stream Settings"));
2256     htsmsg_add_s32 (conf, "priority", PROFILE_SPRIO_IMPORTANT);
2257     htsmsg_add_bool(conf, "shield", 1);
2258     (void)profile_create(NULL, conf, 1);
2259     htsmsg_destroy(conf);
2260   }
2261 
2262   name = "audio";
2263   pro = profile_find_by_name2(name, NULL, 1);
2264   if (pro == NULL || strcmp(profile_get_name(pro), name)) {
2265     htsmsg_t *conf;
2266 
2267     conf = htsmsg_create_map();
2268     htsmsg_add_str (conf, "class", "profile-audio");
2269     htsmsg_add_bool(conf, "enabled", 1);
2270     htsmsg_add_str (conf, "name", name);
2271     htsmsg_add_str (conf, "comment", _("Audio-only stream"));
2272     htsmsg_add_s32 (conf, "priority", PROFILE_SPRIO_NORMAL);
2273     htsmsg_add_bool(conf, "shield", 1);
2274     (void)profile_create(NULL, conf, 1);
2275     htsmsg_destroy(conf);
2276   }
2277 
2278 #if ENABLE_LIBAV
2279 
2280   name = "webtv-vp8-vorbis-webm";
2281   pro = profile_find_by_name2(name, NULL, 1);
2282   if (pro == NULL || strcmp(profile_get_name(pro), name)) {
2283     htsmsg_t *conf;
2284 
2285     conf = htsmsg_create_map();
2286     htsmsg_add_str (conf, "class", "profile-transcode");
2287     htsmsg_add_bool(conf, "enabled", 1);
2288     htsmsg_add_str (conf, "name", name);
2289     htsmsg_add_str (conf, "comment", _("WEBTV profile VP8/Vorbis/WEBM"));
2290     htsmsg_add_s32 (conf, "priority", PROFILE_SPRIO_NORMAL);
2291     htsmsg_add_s32 (conf, "container", MC_WEBM);
2292     htsmsg_add_u32 (conf, "resolution", 384);
2293     htsmsg_add_u32 (conf, "channels", 2);
2294     htsmsg_add_str (conf, "vcodec", "libvpx");
2295     htsmsg_add_str (conf, "vcodec_preset", "faster");
2296     htsmsg_add_str (conf, "acodec", "libvorbis");
2297     htsmsg_add_bool(conf, "shield", 1);
2298     (void)profile_create(NULL, conf, 1);
2299     htsmsg_destroy(conf);
2300   }
2301   name = "webtv-h264-aac-mpegts";
2302   pro = profile_find_by_name2(name, NULL, 1);
2303   if (pro == NULL || strcmp(profile_get_name(pro), name)) {
2304     htsmsg_t *conf;
2305 
2306     conf = htsmsg_create_map();
2307     htsmsg_add_str (conf, "class", "profile-transcode");
2308     htsmsg_add_bool(conf, "enabled", 1);
2309     htsmsg_add_str (conf, "name", name);
2310     htsmsg_add_str (conf, "comment", _("WEBTV profile H264/AAC/MPEG-TS"));
2311     htsmsg_add_s32 (conf, "priority", PROFILE_SPRIO_NORMAL);
2312     htsmsg_add_s32 (conf, "container", MC_MPEGTS);
2313     htsmsg_add_u32 (conf, "resolution", 384);
2314     htsmsg_add_u32 (conf, "channels", 2);
2315     htsmsg_add_str (conf, "vcodec", "libx264");
2316     htsmsg_add_str (conf, "vcodec_preset", "faster");
2317     htsmsg_add_str (conf, "acodec", "aac");
2318     htsmsg_add_bool(conf, "shield", 1);
2319     (void)profile_create(NULL, conf, 1);
2320     htsmsg_destroy(conf);
2321   }
2322   name = "webtv-h264-aac-matroska";
2323   pro = profile_find_by_name2(name, NULL, 1);
2324   if (pro == NULL || strcmp(profile_get_name(pro), name)) {
2325     htsmsg_t *conf;
2326 
2327     conf = htsmsg_create_map();
2328     htsmsg_add_str (conf, "class", "profile-transcode");
2329     htsmsg_add_bool(conf, "enabled", 1);
2330     htsmsg_add_str (conf, "name", name);
2331     htsmsg_add_str (conf, "comment", _("WEBTV profile H264/AAC/Matroska"));
2332     htsmsg_add_s32 (conf, "priority", PROFILE_SPRIO_NORMAL);
2333     htsmsg_add_s32 (conf, "container", MC_MATROSKA);
2334     htsmsg_add_u32 (conf, "resolution", 384);
2335     htsmsg_add_u32 (conf, "channels", 2);
2336     htsmsg_add_str (conf, "vcodec", "libx264");
2337     htsmsg_add_str (conf, "vcodec_preset", "faster");
2338     htsmsg_add_str (conf, "acodec", "aac");
2339     htsmsg_add_bool(conf, "shield", 1);
2340     (void)profile_create(NULL, conf, 1);
2341     htsmsg_destroy(conf);
2342   }
2343 #endif
2344 
2345   /* Assign the default profile if config files are corrupted */
2346   if (!profile_default) {
2347     pro = profile_find_by_name2("pass", NULL, 1);
2348     assert(pro);
2349     profile_default = pro;
2350   }
2351 }
2352 
2353 void
profile_done(void)2354 profile_done(void)
2355 {
2356   profile_t *pro;
2357   profile_build_t *pb;
2358 
2359   pthread_mutex_lock(&global_lock);
2360   profile_default = NULL;
2361   while ((pro = TAILQ_FIRST(&profiles)) != NULL)
2362     profile_delete(pro, 0);
2363   while ((pb = LIST_FIRST(&profile_builders)) != NULL) {
2364     LIST_REMOVE(pb, link);
2365     free(pb);
2366   }
2367   pthread_mutex_unlock(&global_lock);
2368 }
2369