static void handle_sigill(int x) { /* Note that on some platforms, the SSL library tries */ /* to determine the CPU capabilities with possible */ /* unknown instructions */ tvhwarn("CPU", "Illegal instruction handler (might be OK)"); signal(SIGILL, handle_sigill); }
static void satip_device_hack( satip_device_t *sd ) { if (sd->sd_info.deviceid[0] && strcmp(sd->sd_info.server, "Linux/1.0 UPnP/1.1 IDL4K/1.0") == 0) { /* AXE Linux distribution - Inverto firmware */ /* version V1.13.0.105 and probably less */ /* really ugly firmware - soooooo much restrictions */ sd->sd_fullmux_ok = 0; sd->sd_pids_max = 32; sd->sd_pids_deladd = 0; tvhwarn("satip", "Detected old Inverto firmware V1.13.0.105 and less"); tvhwarn("satip", "Upgrade to V1.16.0.120 - http://http://www.inverto.tv/support/ - IDL400s"); } else if (strstr(sd->sd_info.location, ":8888/octonet.xml")) { /* OctopusNet requires pids in the SETUP RTSP command */ sd->sd_pids0 = 1; } else if (strstr(sd->sd_info.manufacturer, "Triax") && strstr(sd->sd_info.modelname, "TSS400")) { sd->sd_pilot_on = 1; } }
/** * Close the muxer and append trailer to output */ static int lav_muxer_close(muxer_t *m) { int ret = 0; lav_muxer_t *lm = (lav_muxer_t*)m; if(lm->lm_init && av_write_trailer(lm->lm_oc) < 0) { tvhwarn(LS_LIBAV, "Failed to write %s trailer", muxer_container_type2txt(lm->m_config.m_type)); lm->m_errors++; ret = -1; } return ret; }
static void epggrab_ota_done ( epggrab_ota_mux_t *om, int reason ) { static const char *reasons[] = { [EPGGRAB_OTA_DONE_COMPLETE] = "complete", [EPGGRAB_OTA_DONE_TIMEOUT] = "timeout", [EPGGRAB_OTA_DONE_NO_DATA] = "no data", [EPGGRAB_OTA_DONE_STOLEN] = "stolen" }; char ubuf[UUID_HEX_SIZE]; mpegts_mux_t *mm; epggrab_ota_map_t *map; if (om->om_save) epggrab_ota_save(om); mm = mpegts_mux_find0(&om->om_mux_uuid); if (mm == NULL) { tvhdebug(LS_EPGGRAB, "unable to find mux %s (grab done: %s)", uuid_get_hex(&om->om_mux_uuid, ubuf), reasons[reason]); return; } tvhdebug(LS_EPGGRAB, "grab done for %s (%s)", mm->mm_nicename, reasons[reason]); mtimer_disarm(&om->om_timer); mtimer_disarm(&om->om_data_timer); mtimer_disarm(&om->om_handlers_timer); assert(om->om_q_type == EPGGRAB_OTA_MUX_ACTIVE); TAILQ_REMOVE(&epggrab_ota_active, om, om_q_link); om->om_q_type = EPGGRAB_OTA_MUX_IDLE; LIST_FOREACH(map, &om->om_modules, om_link) if (map->om_module->stop) map->om_module->stop(map, mm); if (reason == EPGGRAB_OTA_DONE_STOLEN) { /* Do not requeue completed muxes */ if (!om->om_done && om->om_requeue) { TAILQ_INSERT_HEAD(&epggrab_ota_pending, om, om_q_link); om->om_q_type = EPGGRAB_OTA_MUX_PENDING; } else { om->om_requeue = 0; } } else if (reason == EPGGRAB_OTA_DONE_TIMEOUT) { om->om_requeue = 0; LIST_FOREACH(map, &om->om_modules, om_link) if (!map->om_complete) tvhwarn(LS_EPGGRAB, "%s - data completion timeout for %s", map->om_module->name, mm->mm_nicename); } else {
udp_connection_t * udp_sendinit ( int subsystem, const char *name, const char *ifname, int txsize ) { int fd, ifindex; udp_connection_t *uc; uc = calloc(1, sizeof(udp_connection_t)); uc->fd = -1; uc->ifname = ifname ? strdup(ifname) : NULL; uc->subsystem = subsystem; uc->name = name ? strdup(name) : NULL; uc->rxtxsize = txsize; /* Open socket */ if ((fd = tvh_socket(uc->ip.ss_family, SOCK_DGRAM, 0)) == -1) { tvherror(subsystem, "%s - failed to create socket [%s]", name, strerror(errno)); udp_close(uc); return UDP_FATAL_ERROR; } uc->fd = fd; /* Bind to interface */ ifindex = udp_ifindex_required(uc) ? udp_get_ifindex(ifname) : 0; if (ifindex < 0) { tvherror(subsystem, "%s - could not find interface %s", name, ifname); goto error; } if (uc->multicast) { if (uc->ip.ss_family == AF_INET) { #if !defined(PLATFORM_DARWIN) struct ip_mreqn m; memset(&m, 0, sizeof(m)); m.imr_ifindex = ifindex; #else struct in_addr m; if (udp_get_ifaddr(fd, ifname, &m) == -1) { tvherror(subsystem, "%s - cannot find ip address for interface %s [e=%s]", name, ifname, strerror(errno)); goto error; } #endif if (setsockopt(fd, udp_get_solip(), IP_MULTICAST_IF, &m, sizeof(m))) tvhwarn(subsystem, "%s - cannot set source interface %s [%s]", name, ifname, strerror(errno)); } else { struct ipv6_mreq m; memset(&m, 0, sizeof(m)); m.ipv6mr_interface = ifindex; #ifdef SOL_IPV6 if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_IF, &m, sizeof(m))) { tvhwarn(subsystem, "%s - cannot set source interface %s [%s]", name, ifname, strerror(errno)); } #else tvherror(subsystem, "IPv6 multicast not supported"); goto error; #endif } } /* Increase TX buffer size */ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &txsize, sizeof(txsize)) == -1) tvhwarn(subsystem, "%s - cannot increase UDP tx buffer size [%s]", name, strerror(errno)); return uc; error: udp_close(uc); return NULL; }
udp_connection_t * udp_bind ( int subsystem, const char *name, const char *bindaddr, int port, const char *multicast_src, const char *ifname, int rxsize, int txsize ) { int fd, ifindex, reuse = 1; udp_connection_t *uc; char buf[256]; socklen_t addrlen; uc = calloc(1, sizeof(udp_connection_t)); uc->fd = -1; uc->host = bindaddr ? strdup(bindaddr) : NULL; uc->port = port; uc->ifname = ifname ? strdup(ifname) : NULL; uc->subsystem = subsystem; uc->name = name ? strdup(name) : NULL; uc->rxtxsize = rxsize; if (udp_resolve(uc, &uc->ip, uc->host, port, &uc->multicast, 1)) { udp_close(uc); return UDP_FATAL_ERROR; } /* Open socket */ if ((fd = tvh_socket(uc->ip.ss_family, SOCK_DGRAM, 0)) == -1) { tvherror(subsystem, "%s - failed to create socket [%s]", name, strerror(errno)); udp_close(uc); return UDP_FATAL_ERROR; } uc->fd = fd; /* Mark reuse address */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) { tvherror(subsystem, "%s - failed to reuse address for socket [%s]", name, strerror(errno)); udp_close(uc); return UDP_FATAL_ERROR; } /* Bind to interface */ ifindex = udp_ifindex_required(uc) ? udp_get_ifindex(ifname) : 0; if (ifindex < 0) { tvherror(subsystem, "%s - could not find interface %s", name, ifname); goto error; } /* IPv4 */ if (uc->ip.ss_family == AF_INET) { /* Bind */ if (bind(fd, (struct sockaddr *)&uc->ip, sizeof(struct sockaddr_in))) { inet_ntop(AF_INET, &IP_AS_V4(uc->ip, addr), buf, sizeof(buf)); tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", name, buf, ntohs(IP_AS_V4(uc->ip, port)), strerror(errno)); goto error; } if (uc->multicast) { /* Join multicast group */ if (multicast_src && *multicast_src) { /* Join with specific source address (SSM) */ struct ip_mreq_source ms; memset(&ms, 0, sizeof(ms)); ms.imr_multiaddr = IP_AS_V4(uc->ip, addr); /* Note, ip_mreq_source does not support the ifindex parameter, so we have to resolve to the ip of the interface on all platforms. */ if (udp_get_ifaddr(fd, ifname, &ms.imr_interface) == -1) { tvherror(subsystem, "%s - cannot find ip address for interface %s [e=%s]", name, ifname, strerror(errno)); goto error; } if (inet_pton(AF_INET, multicast_src, &ms.imr_sourceaddr) < 1) { tvherror(subsystem, "%s - invalid ipv4 address '%s' specified as multicast source [e=%s]", name, multicast_src, strerror(errno)); goto error; } if (setsockopt(fd, udp_get_solip(), IP_ADD_SOURCE_MEMBERSHIP, &ms, sizeof(ms)) < 0) { tvherror(subsystem, "%s - setsockopt IP_ADD_SOURCE_MEMBERSHIP failed [e=%s]", name, strerror(errno)); goto error; } } else { /* Standard multicast join (non-SSM) */ #if defined(PLATFORM_DARWIN) struct ip_mreq m; #else struct ip_mreqn m; #endif memset(&m, 0, sizeof(m)); m.imr_multiaddr = IP_AS_V4(uc->ip, addr); #if !defined(PLATFORM_DARWIN) m.imr_address.s_addr = 0; m.imr_ifindex = ifindex; #else if (udp_get_ifaddr(fd, ifname, &m.imr_interface) == -1) { tvherror(subsystem, "%s - cannot find ip address for interface %s [e=%s]", name, ifname, strerror(errno)); goto error; } #endif if (setsockopt(fd, udp_get_solip(), IP_ADD_MEMBERSHIP, &m, sizeof(m))) { inet_ntop(AF_INET, &m.imr_multiaddr, buf, sizeof(buf)); tvhwarn(subsystem, "%s - cannot join %s [%s]", name, buf, strerror(errno)); } } } /* Bind to IPv6 group */ } else { struct ipv6_mreq m; memset(&m, 0, sizeof(m)); /* Bind */ if (bind(fd, (struct sockaddr *)&uc->ip, sizeof(struct sockaddr_in6))) { inet_ntop(AF_INET6, &IP_AS_V6(uc->ip, addr), buf, sizeof(buf)); tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", name, buf, ntohs(IP_AS_V6(uc->ip, port)), strerror(errno)); goto error; } if (uc->multicast) { /* Join group */ m.ipv6mr_multiaddr = IP_AS_V6(uc->ip, addr); m.ipv6mr_interface = ifindex; #ifdef SOL_IPV6 if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m, sizeof(m))) { inet_ntop(AF_INET, &m.ipv6mr_multiaddr, buf, sizeof(buf)); tvhwarn(subsystem, "%s - cannot join %s [%s]", name, buf, strerror(errno)); } #else tvherror(subsystem, "IPv6 multicast not supported"); goto error; #endif } } addrlen = sizeof(uc->ip); if (getsockname(fd, (struct sockaddr *)&uc->ip, &addrlen)) { tvherror(subsystem, "%s - cannot obtain socket name [%s]", name, strerror(errno)); goto error; } /* Increase/Decrease RX buffer size */ if (rxsize > 0 && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rxsize, sizeof(rxsize)) == -1) tvhwarn(subsystem, "%s - cannot change UDP rx buffer size [%s]", name, strerror(errno)); /* Increase/Decrease TX buffer size */ if (txsize > 0 && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &txsize, sizeof(txsize)) == -1) tvhwarn(subsystem, "%s - cannot change UDP tx buffer size [%s]", name, strerror(errno)); return uc; error: udp_close(uc); return NULL; }
int main(int argc, char **argv) { int i; sigset_t set; #if ENABLE_MPEGTS uint32_t adapter_mask = 0; #endif int log_level = LOG_INFO; int log_options = TVHLOG_OPT_MILLIS | TVHLOG_OPT_STDERR | TVHLOG_OPT_SYSLOG; const char *log_debug = NULL, *log_trace = NULL; gid_t gid = -1; uid_t uid = -1; char buf[512]; FILE *pidfile = NULL; extern int dvb_bouquets_parse; main_tid = pthread_self(); /* Setup global mutexes */ pthread_mutex_init(&fork_lock, NULL); pthread_mutex_init(&global_lock, NULL); pthread_mutex_init(&tasklet_lock, NULL); pthread_mutex_init(&atomic_lock, NULL); pthread_cond_init(>imer_cond, NULL); pthread_cond_init(&tasklet_cond, NULL); TAILQ_INIT(&tasklets); /* Defaults */ tvheadend_webui_port = 9981; tvheadend_webroot = NULL; tvheadend_htsp_port = 9982; tvheadend_htsp_port_extra = 0; time(&dispatch_clock); /* Command line options */ int opt_help = 0, opt_version = 0, opt_fork = 0, opt_firstrun = 0, opt_stderr = 0, opt_syslog = 0, opt_nosyslog = 0, opt_uidebug = 0, opt_abort = 0, opt_noacl = 0, opt_fileline = 0, opt_threadid = 0, opt_libav = 0, opt_ipv6 = 0, opt_satip_rtsp = 0, #if ENABLE_TSFILE opt_tsfile_tuner = 0, #endif opt_dump = 0, opt_xspf = 0, opt_dbus = 0, opt_dbus_session = 0, opt_nobackup = 0, opt_nobat = 0; const char *opt_config = NULL, *opt_user = NULL, *opt_group = NULL, *opt_logpath = NULL, *opt_log_debug = NULL, *opt_log_trace = NULL, *opt_pidpath = "/var/run/tvheadend.pid", #if ENABLE_LINUXDVB *opt_dvb_adapters = NULL, #endif *opt_bindaddr = NULL, *opt_subscribe = NULL, *opt_user_agent = NULL; str_list_t opt_satip_xml = { .max = 10, .num = 0, .str = calloc(10, sizeof(char*)) }; str_list_t opt_tsfile = { .max = 10, .num = 0, .str = calloc(10, sizeof(char*)) }; cmdline_opt_t cmdline_opts[] = { { 0, NULL, N_("Generic Options"), OPT_BOOL, NULL }, { 'h', "help", N_("Show this page"), OPT_BOOL, &opt_help }, { 'v', "version", N_("Show version information"),OPT_BOOL, &opt_version }, { 0, NULL, N_("Service Configuration"), OPT_BOOL, NULL }, { 'c', "config", N_("Alternate config path"), OPT_STR, &opt_config }, { 'B', "nobackup", N_("Don't backup config tree at upgrade"), OPT_BOOL, &opt_nobackup }, { 'f', "fork", N_("Fork and run as daemon"), OPT_BOOL, &opt_fork }, { 'u', "user", N_("Run as user"), OPT_STR, &opt_user }, { 'g', "group", N_("Run as group"), OPT_STR, &opt_group }, { 'p', "pid", N_("Alternate pid path"), OPT_STR, &opt_pidpath }, { 'C', "firstrun", N_("If no user account exists then create one with\n" "no username and no password. Use with care as\n" "it will allow world-wide administrative access\n" "to your Tvheadend installation until you edit/create\n" "access-control from within the Tvheadend UI"), OPT_BOOL, &opt_firstrun }, #if ENABLE_DBUS_1 { 'U', "dbus", N_("Enable DBus"), OPT_BOOL, &opt_dbus }, { 'e', "dbus_session", N_("DBus - use the session message bus instead system one"), OPT_BOOL, &opt_dbus_session }, #endif #if ENABLE_LINUXDVB { 'a', "adapters", N_("Only use specified DVB adapters (comma separated)"), OPT_STR, &opt_dvb_adapters }, #endif #if ENABLE_SATIP_SERVER { 0, "satip_rtsp", N_("SAT>IP RTSP port number for server\n" "(default: -1 = disable, 0 = webconfig, standard port is 554)"), OPT_INT, &opt_satip_rtsp }, #endif #if ENABLE_SATIP_CLIENT { 0, "satip_xml", N_("URL with the SAT>IP server XML location"), OPT_STR_LIST, &opt_satip_xml }, #endif { 0, NULL, N_("Server Connectivity"), OPT_BOOL, NULL }, { '6', "ipv6", N_("Listen on IPv6"), OPT_BOOL, &opt_ipv6 }, { 'b', "bindaddr", N_("Specify bind address"), OPT_STR, &opt_bindaddr}, { 0, "http_port", N_("Specify alternative http port"), OPT_INT, &tvheadend_webui_port }, { 0, "http_root", N_("Specify alternative http webroot"), OPT_STR, &tvheadend_webroot }, { 0, "htsp_port", N_("Specify alternative htsp port"), OPT_INT, &tvheadend_htsp_port }, { 0, "htsp_port2", N_("Specify extra htsp port"), OPT_INT, &tvheadend_htsp_port_extra }, { 0, "useragent", N_("Specify User-Agent header for the http client"), OPT_STR, &opt_user_agent }, { 0, "xspf", N_("Use XSPF playlist instead of M3U"), OPT_BOOL, &opt_xspf }, { 0, NULL, N_("Debug Options"), OPT_BOOL, NULL }, { 'd', "stderr", N_("Enable debug on stderr"), OPT_BOOL, &opt_stderr }, { 's', "syslog", N_("Enable debug to syslog"), OPT_BOOL, &opt_syslog }, { 'S', "nosyslog", N_("Disable syslog (all msgs)"), OPT_BOOL, &opt_nosyslog }, { 'l', "logfile", N_("Enable debug to file"), OPT_STR, &opt_logpath }, { 0, "debug", N_("Enable debug subsystems"), OPT_STR, &opt_log_debug }, #if ENABLE_TRACE { 0, "trace", N_("Enable trace subsystems"), OPT_STR, &opt_log_trace }, #endif { 0, "fileline", N_("Add file and line numbers to debug"), OPT_BOOL, &opt_fileline }, { 0, "threadid", N_("Add the thread ID to debug"), OPT_BOOL, &opt_threadid }, #if ENABLE_LIBAV { 0, "libav", N_("More verbose libav log"), OPT_BOOL, &opt_libav }, #endif { 0, "uidebug", N_("Enable webUI debug (non-minified JS)"), OPT_BOOL, &opt_uidebug }, { 'A', "abort", N_("Immediately abort"), OPT_BOOL, &opt_abort }, { 'D', "dump", N_("Enable coredumps for daemon"), OPT_BOOL, &opt_dump }, { 0, "noacl", N_("Disable all access control checks"), OPT_BOOL, &opt_noacl }, { 0, "nobat", N_("Disable DVB bouquets"), OPT_BOOL, &opt_nobat }, { 'j', "join", N_("Subscribe to a service permanently"), OPT_STR, &opt_subscribe }, #if ENABLE_TSFILE || ENABLE_TSDEBUG { 0, NULL, N_("Testing options"), OPT_BOOL, NULL }, { 0, "tsfile_tuners", N_("Number of tsfile tuners"), OPT_INT, &opt_tsfile_tuner }, { 0, "tsfile", N_("tsfile input (mux file)"), OPT_STR_LIST, &opt_tsfile }, #endif #if ENABLE_TSDEBUG { 0, "tsdebug", N_("Output directory for tsdebug"), OPT_STR, &tvheadend_tsdebug }, #endif }; /* Get current directory */ tvheadend_cwd0 = dirname(tvh_strdupa(argv[0])); tvheadend_cwd = dirname(tvh_strdupa(tvheadend_cwd0)); /* Set locale */ setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); /* make sure the timezone is set */ tzset(); /* Process command line */ for (i = 1; i < argc; i++) { /* Find option */ cmdline_opt_t *opt = cmdline_opt_find(cmdline_opts, ARRAY_SIZE(cmdline_opts), argv[i]); if (!opt) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), _("invalid option specified [%s]"), argv[i]); /* Process */ if (opt->type == OPT_BOOL) *((int*)opt->param) = 1; else if (++i == argc) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), _("option %s requires a value"), opt->lopt); else if (opt->type == OPT_INT) *((int*)opt->param) = atoi(argv[i]); else if (opt->type == OPT_STR_LIST) { str_list_t *strl = opt->param; if (strl->num < strl->max) strl->str[strl->num++] = argv[i]; } else *((char**)opt->param) = argv[i]; /* Stop processing */ if (opt_help) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), NULL); if (opt_version) show_version(argv[0]); } /* Additional cmdline processing */ if (opt_nobat) dvb_bouquets_parse = 0; #if ENABLE_LINUXDVB if (!opt_dvb_adapters) { adapter_mask = ~0; } else { char *p, *e; char *r = NULL; char *dvb_adapters = strdup(opt_dvb_adapters); adapter_mask = 0x0; p = strtok_r(dvb_adapters, ",", &r); while (p) { int a = strtol(p, &e, 10); if (*e != 0 || a < 0 || a > 31) { fprintf(stderr, _("Invalid adapter number '%s'\n"), p); free(dvb_adapters); return 1; } adapter_mask |= (1 << a); p = strtok_r(NULL, ",", &r); } free(dvb_adapters); if (!adapter_mask) { fprintf(stderr, "%s", _("No adapters specified!\n")); return 1; } } #endif if (tvheadend_webroot) { char *tmp; if (*tvheadend_webroot == '/') tmp = strdup(tvheadend_webroot); else { tmp = malloc(strlen(tvheadend_webroot)+2); *tmp = '/'; strcpy(tmp+1, tvheadend_webroot); } if (tmp[strlen(tmp)-1] == '/') tmp[strlen(tmp)-1] = '\0'; tvheadend_webroot = tmp; } tvheadend_webui_debug = opt_uidebug; /* Setup logging */ if (isatty(2)) log_options |= TVHLOG_OPT_DECORATE; if (opt_stderr || opt_syslog || opt_logpath) { if (!opt_log_trace && !opt_log_debug) log_debug = "all"; log_level = LOG_DEBUG; if (opt_stderr) log_options |= TVHLOG_OPT_DBG_STDERR; if (opt_syslog) log_options |= TVHLOG_OPT_DBG_SYSLOG; if (opt_logpath) log_options |= TVHLOG_OPT_DBG_FILE; } if (opt_nosyslog) log_options &= ~(TVHLOG_OPT_SYSLOG|TVHLOG_OPT_DBG_SYSLOG); if (opt_fileline) log_options |= TVHLOG_OPT_FILELINE; if (opt_threadid) log_options |= TVHLOG_OPT_THREAD; if (opt_libav) log_options |= TVHLOG_OPT_LIBAV; if (opt_log_trace) { log_level = LOG_TRACE; log_trace = opt_log_trace; } if (opt_log_debug) log_debug = opt_log_debug; tvhlog_init(log_level, log_options, opt_logpath); tvhlog_set_debug(log_debug); tvhlog_set_trace(log_trace); tvhinfo("main", "Log started"); signal(SIGPIPE, handle_sigpipe); // will be redundant later signal(SIGILL, handle_sigill); // see handler.. /* Set priviledges */ if(opt_fork || opt_group || opt_user) { const char *homedir; struct group *grp = getgrnam(opt_group ?: "video"); struct passwd *pw = opt_user ? getpwnam(opt_user) : NULL; if(grp != NULL) { gid = grp->gr_gid; } else { gid = 1; } if (pw != NULL) { if (getuid() != pw->pw_uid) { gid_t glist[16]; int gnum; gnum = get_user_groups(pw, glist, ARRAY_SIZE(glist)); if (gnum > 0 && setgroups(gnum, glist)) { char buf[256] = ""; int i; for (i = 0; i < gnum; i++) snprintf(buf + strlen(buf), sizeof(buf) - 1 - strlen(buf), ",%d", glist[i]); tvhlog(LOG_ALERT, "START", "setgroups(%s) failed, do you have permission?", buf+1); return 1; } } uid = pw->pw_uid; homedir = pw->pw_dir; setenv("HOME", homedir, 1); } else { uid = 1; } } uuid_init(); config_boot(opt_config, gid, uid); tcp_server_preinit(opt_ipv6); http_server_init(opt_bindaddr); // bind to ports only htsp_init(opt_bindaddr); // bind to ports only satip_server_init(opt_satip_rtsp); // bind to ports only if (opt_fork) pidfile = tvh_fopen(opt_pidpath, "w+"); if (gid != -1 && (getgid() != gid) && setgid(gid)) { tvhlog(LOG_ALERT, "START", "setgid(%d) failed, do you have permission?", gid); return 1; } if (uid != -1 && (getuid() != uid) && setuid(uid)) { tvhlog(LOG_ALERT, "START", "setuid(%d) failed, do you have permission?", uid); return 1; } /* Daemonise */ if(opt_fork) { if(daemon(0, 0)) { exit(2); } if(pidfile != NULL) { fprintf(pidfile, "%d\n", getpid()); fclose(pidfile); } /* Make dumpable */ if (opt_dump) { #ifdef PLATFORM_LINUX if (chdir("/tmp")) tvhwarn("START", "failed to change cwd to /tmp"); prctl(PR_SET_DUMPABLE, 1); #else tvhwarn("START", "Coredumps not implemented on your platform"); #endif } umask(0); } tvheadend_running = 1; /* Start log thread (must be done post fork) */ tvhlog_start(); /* Alter logging */ if (opt_fork) tvhlog_options &= ~TVHLOG_OPT_STDERR; if (!isatty(2)) tvhlog_options &= ~TVHLOG_OPT_DECORATE; /* Initialise clock */ pthread_mutex_lock(&global_lock); time(&dispatch_clock); /* Signal handling */ sigfillset(&set); sigprocmask(SIG_BLOCK, &set, NULL); trap_init(argv[0]); /* SSL library init */ OPENSSL_config(NULL); SSL_load_error_strings(); SSL_library_init(); /* Initialise configuration */ notify_init(); idnode_init(); spawn_init(); config_init(opt_nobackup == 0); /** * Initialize subsystems */ epg_in_load = 1; tvhthread_create(&tasklet_tid, NULL, tasklet_thread, NULL); dbus_server_init(opt_dbus, opt_dbus_session); intlconv_init(); api_init(); fsmonitor_init(); libav_init(); tvhtime_init(); profile_init(); imagecache_init(); http_client_init(opt_user_agent); esfilter_init(); bouquet_init(); service_init(); dvb_init(); #if ENABLE_MPEGTS mpegts_init(adapter_mask, &opt_satip_xml, &opt_tsfile, opt_tsfile_tuner); #endif channel_init(); bouquet_service_resolve(); subscription_init(); dvr_config_init(); access_init(opt_firstrun, opt_noacl); #if ENABLE_TIMESHIFT timeshift_init(); #endif tcp_server_init(); webui_init(opt_xspf); #if ENABLE_UPNP upnp_server_init(opt_bindaddr); #endif service_mapper_init(); descrambler_init(); epggrab_init(); epg_init(); dvr_init(); dbus_server_start(); http_server_register(); satip_server_register(); htsp_register(); if(opt_subscribe != NULL) subscription_dummy_join(opt_subscribe, 1); avahi_init(); bonjour_init(); epg_updated(); // cleanup now all prev ref's should have been created epg_in_load = 0; pthread_mutex_unlock(&global_lock); /** * Wait for SIGTERM / SIGINT, but only in this thread */ sigemptyset(&set); sigaddset(&set, SIGTERM); sigaddset(&set, SIGINT); signal(SIGTERM, doexit); signal(SIGINT, doexit); pthread_sigmask(SIG_UNBLOCK, &set, NULL); tvhlog(LOG_NOTICE, "START", "HTS Tvheadend version %s started, " "running as PID:%d UID:%d GID:%d, CWD:%s CNF:%s", tvheadend_version, getpid(), getuid(), getgid(), getcwd(buf, sizeof(buf)), hts_settings_get_root()); if(opt_abort) abort(); mainloop(); #if ENABLE_DBUS_1 tvhftrace("main", dbus_server_done); #endif #if ENABLE_UPNP tvhftrace("main", upnp_server_done); #endif tvhftrace("main", satip_server_done); tvhftrace("main", htsp_done); tvhftrace("main", http_server_done); tvhftrace("main", webui_done); tvhftrace("main", fsmonitor_done); tvhftrace("main", http_client_done); tvhftrace("main", tcp_server_done); // Note: the locking is obviously a bit redundant, but without // we need to disable the gtimer_arm call in epg_save() pthread_mutex_lock(&global_lock); tvhftrace("main", epg_save); #if ENABLE_TIMESHIFT tvhftrace("main", timeshift_term); #endif pthread_mutex_unlock(&global_lock); tvhftrace("main", epggrab_done); #if ENABLE_MPEGTS tvhftrace("main", mpegts_done); #endif tvhftrace("main", descrambler_done); tvhftrace("main", service_mapper_done); tvhftrace("main", service_done); tvhftrace("main", channel_done); tvhftrace("main", bouquet_done); tvhftrace("main", dvr_done); tvhftrace("main", subscription_done); tvhftrace("main", access_done); tvhftrace("main", epg_done); tvhftrace("main", avahi_done); tvhftrace("main", bonjour_done); tvhftrace("main", imagecache_done); tvhftrace("main", lang_code_done); tvhftrace("main", api_done); tvhtrace("main", "tasklet enter"); pthread_cond_signal(&tasklet_cond); pthread_join(tasklet_tid, NULL); tvhtrace("main", "tasklet thread end"); tasklet_flush(); tvhtrace("main", "tasklet leave"); tvhftrace("main", hts_settings_done); tvhftrace("main", dvb_done); tvhftrace("main", lang_str_done); tvhftrace("main", esfilter_done); tvhftrace("main", profile_done); tvhftrace("main", intlconv_done); tvhftrace("main", urlparse_done); tvhftrace("main", idnode_done); tvhftrace("main", notify_done); tvhftrace("main", spawn_done); tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); tvhlog_end(); tvhftrace("main", config_done); if(opt_fork) unlink(opt_pidpath); #if ENABLE_TSFILE free(opt_tsfile.str); #endif free(opt_satip_xml.str); /* OpenSSL - welcome to the "cleanup" hell */ ENGINE_cleanup(); RAND_cleanup(); CRYPTO_cleanup_all_ex_data(); EVP_cleanup(); CONF_modules_free(); #ifndef OPENSSL_NO_COMP COMP_zlib_cleanup(); #endif ERR_remove_state(0); ERR_free_strings(); #ifndef OPENSSL_NO_COMP sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); #endif /* end of OpenSSL cleanup code */ #if ENABLE_DBUS_1 extern void dbus_shutdown(void); if (opt_dbus) dbus_shutdown(); #endif return 0; } /** * */ void tvh_str_set(char **strp, const char *src) { free(*strp); *strp = src ? strdup(src) : NULL; } /** * */ int tvh_str_update(char **strp, const char *src) { if(src == NULL) return 0; free(*strp); *strp = strdup(src); return 1; } /** * */ void scopedunlock(pthread_mutex_t **mtxp) { pthread_mutex_unlock(*mtxp); }
int rtsp_setup_decode( http_client_t *hc, int satip ) { char *argv[32], *argv2[2], *p; int i, n, j; #if 0 { http_arg_t *ra; TAILQ_FOREACH(ra, &hc->hc_args, link) printf(" %s: %s\n", ra->key, ra->val); } #endif rtsp_clear_session(hc); if (hc->hc_code != 200) return -EIO; p = http_arg_get(&hc->hc_args, "Session"); if (p == NULL) return -EIO; n = http_tokenize(p, argv, 32, ';'); if (n < 1) return -EIO; hc->hc_rtsp_session = strdup(argv[0]); for (i = 1; i < n; i++) { if (strncasecmp(argv[i], "timeout=", 8) == 0) { hc->hc_rtp_timeout = atoi(argv[i] + 8); if (hc->hc_rtp_timeout < 20 || hc->hc_rtp_timeout > 3600) { tvhwarn(LS_RTSP, "timeout value out of range 20-3600 (%i)", hc->hc_rtp_timeout); return -EIO; } } } if (satip) { p = http_arg_get(&hc->hc_args, "com.ses.streamID"); if (p == NULL) return -EIO; /* zero is valid stream id per specification */ while (*p && ((*p == '0' && *(p + 1) == '0') || *p < ' ')) p++; if (p[0] == '0' && p[1] == '\0') { hc->hc_rtsp_stream_id = 0; } else { hc->hc_rtsp_stream_id = atoll(p); if (hc->hc_rtsp_stream_id <= 0) return -EIO; } } p = http_arg_get(&hc->hc_args, "Transport"); if (p == NULL) return -EIO; n = http_tokenize(p, argv, 32, ';'); if (n < 2) return -EIO; hc->hc_rtp_tcp = -1; hc->hc_rtcp_tcp = -1; hc->hc_rtp_port = -1; hc->hc_rtcp_port = -1; if (!strcasecmp(argv[0], "RTP/AVP/TCP")) { for (i = 1; i < n; i++) { if (strncmp(argv[i], "interleaved=", 12) == 0) { j = http_tokenize(argv[i] + 12, argv2, 2, '-'); if (j > 0) { hc->hc_rtp_tcp = atoi(argv2[0]); if (hc->hc_rtp_tcp < 0) return -EIO; if (j > 1) { hc->hc_rtcp_tcp = atoi(argv2[1]); if (hc->hc_rtcp_tcp < 0) return -EIO; } } else { return -EIO; } } } } else if (!strcasecmp(argv[0], "RTP/AVP") || !strcasecmp(argv[0], "RTP/AVP/UDP")) { if (n < 3) return -EIO; hc->hc_rtp_multicast = strcasecmp(argv[1], "multicast") == 0; if (strcasecmp(argv[1], "unicast") && !hc->hc_rtp_multicast) return -EIO; for (i = 2; i < n; i++) { if (strncmp(argv[i], "destination=", 12) == 0) hc->hc_rtp_dest = strdup(argv[i] + 12); else if (strncmp(argv[i], "client_port=", 12) == 0) { j = http_tokenize(argv[i] + 12, argv2, 2, '-'); if (j > 0) { hc->hc_rtp_port = atoi(argv2[0]); if (hc->hc_rtp_port <= 0) return -EIO; if (j > 1) { hc->hc_rtcp_port = atoi(argv2[1]); if (hc->hc_rtcp_port <= 0) return -EIO; } } else { return -EIO; } } else if (strncmp(argv[i], "server_port=", 12) == 0) { j = http_tokenize(argv[i] + 12, argv2, 2, '-'); if (j > 1) { hc->hc_rtcp_server_port = atoi(argv2[1]); if (hc->hc_rtcp_server_port <= 0) return -EIO; } else { return -EIO; } } } } else { return -EIO; } return HTTP_CON_OK; }
int main(int argc, char **argv) { int i; sigset_t set; #if ENABLE_LINUXDVB uint32_t adapter_mask; #endif int log_level = LOG_INFO; int log_options = TVHLOG_OPT_MILLIS | TVHLOG_OPT_STDERR | TVHLOG_OPT_SYSLOG; const char *log_debug = NULL, *log_trace = NULL; char buf[512]; main_tid = pthread_self(); /* Setup global mutexes */ pthread_mutex_init(&ffmpeg_lock, NULL); pthread_mutex_init(&fork_lock, NULL); pthread_mutex_init(&global_lock, NULL); pthread_mutex_init(&atomic_lock, NULL); pthread_cond_init(>imer_cond, NULL); /* Defaults */ tvheadend_webui_port = 9981; tvheadend_webroot = NULL; tvheadend_htsp_port = 9982; tvheadend_htsp_port_extra = 0; /* Command line options */ int opt_help = 0, opt_version = 0, opt_fork = 0, opt_firstrun = 0, opt_stderr = 0, opt_syslog = 0, opt_uidebug = 0, opt_abort = 0, opt_noacl = 0, opt_fileline = 0, opt_threadid = 0, opt_ipv6 = 0, opt_tsfile_tuner = 0, opt_dump = 0; const char *opt_config = NULL, *opt_user = NULL, *opt_group = NULL, *opt_logpath = NULL, *opt_log_debug = NULL, *opt_log_trace = NULL, *opt_pidpath = "/var/run/tvheadend.pid", #if ENABLE_LINUXDVB *opt_dvb_adapters = NULL, #endif *opt_bindaddr = NULL, *opt_subscribe = NULL; str_list_t opt_tsfile = { .max = 10, .num = 0, .str = calloc(10, sizeof(char*)) }; cmdline_opt_t cmdline_opts[] = { { 0, NULL, "Generic Options", OPT_BOOL, NULL }, { 'h', "help", "Show this page", OPT_BOOL, &opt_help }, { 'v', "version", "Show version infomation", OPT_BOOL, &opt_version }, { 0, NULL, "Service Configuration", OPT_BOOL, NULL }, { 'c', "config", "Alternate config path", OPT_STR, &opt_config }, { 'f', "fork", "Fork and run as daemon", OPT_BOOL, &opt_fork }, { 'u', "user", "Run as user", OPT_STR, &opt_user }, { 'g', "group", "Run as group", OPT_STR, &opt_group }, { 'p', "pid", "Alternate pid path", OPT_STR, &opt_pidpath }, { 'C', "firstrun", "If no user account exists then create one with\n" "no username and no password. Use with care as\n" "it will allow world-wide administrative access\n" "to your Tvheadend installation until you edit\n" "the access-control from within the Tvheadend UI", OPT_BOOL, &opt_firstrun }, #if ENABLE_LINUXDVB { 'a', "adapters", "Only use specified DVB adapters (comma separated)", OPT_STR, &opt_dvb_adapters }, #endif { 0, NULL, "Server Connectivity", OPT_BOOL, NULL }, { '6', "ipv6", "Listen on IPv6", OPT_BOOL, &opt_ipv6 }, { 'b', "bindaddr", "Specify bind address", OPT_STR, &opt_bindaddr}, { 0, "http_port", "Specify alternative http port", OPT_INT, &tvheadend_webui_port }, { 0, "http_root", "Specify alternative http webroot", OPT_STR, &tvheadend_webroot }, { 0, "htsp_port", "Specify alternative htsp port", OPT_INT, &tvheadend_htsp_port }, { 0, "htsp_port2", "Specify extra htsp port", OPT_INT, &tvheadend_htsp_port_extra }, { 0, NULL, "Debug Options", OPT_BOOL, NULL }, { 'd', "stderr", "Enable debug on stderr", OPT_BOOL, &opt_stderr }, { 's', "syslog", "Enable debug to syslog", OPT_BOOL, &opt_syslog }, { 'l', "logfile", "Enable debug to file", OPT_STR, &opt_logpath }, { 0, "debug", "Enable debug subsystems", OPT_STR, &opt_log_debug }, #if ENABLE_TRACE { 0, "trace", "Enable trace subsystems", OPT_STR, &opt_log_trace }, #endif { 0, "fileline", "Add file and line numbers to debug", OPT_BOOL, &opt_fileline }, { 0, "threadid", "Add the thread ID to debug", OPT_BOOL, &opt_threadid }, { 0, "uidebug", "Enable webUI debug (non-minified JS)", OPT_BOOL, &opt_uidebug }, { 'A', "abort", "Immediately abort", OPT_BOOL, &opt_abort }, { 'D', "dump", "Enable coredumps for daemon", OPT_BOOL, &opt_dump }, { 0, "noacl", "Disable all access control checks", OPT_BOOL, &opt_noacl }, { 'j', "join", "Subscribe to a service permanently", OPT_STR, &opt_subscribe }, { 0, NULL, "TODO: testing", OPT_BOOL, NULL }, { 0, "tsfile_tuners", "Number of tsfile tuners", OPT_INT, &opt_tsfile_tuner }, { 0, "tsfile", "tsfile input (mux file)", OPT_STR_LIST, &opt_tsfile }, }; /* Get current directory */ tvheadend_cwd = dirname(dirname(tvh_strdupa(argv[0]))); /* Set locale */ setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); /* make sure the timezone is set */ tzset(); /* Process command line */ for (i = 1; i < argc; i++) { /* Find option */ cmdline_opt_t *opt = cmdline_opt_find(cmdline_opts, ARRAY_SIZE(cmdline_opts), argv[i]); if (!opt) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), "invalid option specified [%s]", argv[i]); /* Process */ if (opt->type == OPT_BOOL) *((int*)opt->param) = 1; else if (++i == argc) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), "option %s requires a value", opt->lopt); else if (opt->type == OPT_INT) *((int*)opt->param) = atoi(argv[i]); else if (opt->type == OPT_STR_LIST) { str_list_t *strl = opt->param; if (strl->num < strl->max) strl->str[strl->num++] = argv[i]; } else *((char**)opt->param) = argv[i]; /* Stop processing */ if (opt_help) show_usage(argv[0], cmdline_opts, ARRAY_SIZE(cmdline_opts), NULL); if (opt_version) show_version(argv[0]); } /* Additional cmdline processing */ #if ENABLE_LINUXDVB if (!opt_dvb_adapters) { adapter_mask = ~0; } else { char *p, *e; char *r = NULL; char *dvb_adapters = strdup(opt_dvb_adapters); adapter_mask = 0x0; p = strtok_r(dvb_adapters, ",", &r); while (p) { int a = strtol(p, &e, 10); if (*e != 0 || a < 0 || a > 31) { tvhlog(LOG_ERR, "START", "Invalid adapter number '%s'", p); free(dvb_adapters); return 1; } adapter_mask |= (1 << a); p = strtok_r(NULL, ",", &r); } free(dvb_adapters); if (!adapter_mask) { tvhlog(LOG_ERR, "START", "No adapters specified!"); return 1; } } #endif if (tvheadend_webroot) { char *tmp; if (*tvheadend_webroot == '/') tmp = strdup(tvheadend_webroot); else { tmp = malloc(strlen(tvheadend_webroot)+2); *tmp = '/'; strcpy(tmp+1, tvheadend_webroot); } if (tmp[strlen(tmp)-1] == '/') tmp[strlen(tmp)-1] = '\0'; tvheadend_webroot = tmp; } tvheadend_webui_debug = opt_uidebug; /* Setup logging */ if (isatty(2)) log_options |= TVHLOG_OPT_DECORATE; if (opt_stderr || opt_syslog || opt_logpath) { if (!opt_log_trace && !opt_log_debug) log_debug = "all"; log_level = LOG_DEBUG; if (opt_stderr) log_options |= TVHLOG_OPT_DBG_STDERR; if (opt_syslog) log_options |= TVHLOG_OPT_DBG_SYSLOG; if (opt_logpath) log_options |= TVHLOG_OPT_DBG_FILE; } if (opt_fileline) log_options |= TVHLOG_OPT_FILELINE; if (opt_threadid) log_options |= TVHLOG_OPT_THREAD; if (opt_log_trace) { log_level = LOG_TRACE; log_trace = opt_log_trace; } if (opt_log_debug) log_debug = opt_log_debug; tvhlog_init(log_level, log_options, opt_logpath); tvhlog_set_debug(log_debug); tvhlog_set_trace(log_trace); signal(SIGPIPE, handle_sigpipe); // will be redundant later /* Daemonise */ if(opt_fork) { const char *homedir; gid_t gid; uid_t uid; struct group *grp = getgrnam(opt_group ?: "video"); struct passwd *pw = opt_user ? getpwnam(opt_user) : NULL; FILE *pidfile = fopen(opt_pidpath, "w+"); if(grp != NULL) { gid = grp->gr_gid; } else { gid = 1; } if (pw != NULL) { if (getuid() != pw->pw_uid) { gid_t glist[10]; int gnum; gnum = get_user_groups(pw, glist, 10); if (setgroups(gnum, glist)) { tvhlog(LOG_ALERT, "START", "setgroups() failed, do you have permission?"); return 1; } } uid = pw->pw_uid; homedir = pw->pw_dir; setenv("HOME", homedir, 1); } else { uid = 1; } if ((getgid() != gid) && setgid(gid)) { tvhlog(LOG_ALERT, "START", "setgid() failed, do you have permission?"); return 1; } if ((getuid() != uid) && setuid(uid)) { tvhlog(LOG_ALERT, "START", "setuid() failed, do you have permission?"); return 1; } if(daemon(0, 0)) { exit(2); } if(pidfile != NULL) { fprintf(pidfile, "%d\n", getpid()); fclose(pidfile); } /* Make dumpable */ if (opt_dump) { #ifdef PLATFORM_LINUX if (chdir("/tmp")) tvhwarn("START", "failed to change cwd to /tmp"); prctl(PR_SET_DUMPABLE, 1); #else tvhwarn("START", "Coredumps not implemented on your platform"); #endif } umask(0); } tvheadend_running = 1; /* Start log thread (must be done post fork) */ tvhlog_start(); /* Alter logging */ if (opt_fork) tvhlog_options &= ~TVHLOG_OPT_STDERR; if (!isatty(2)) tvhlog_options &= ~TVHLOG_OPT_DECORATE; /* Initialise clock */ pthread_mutex_lock(&global_lock); time(&dispatch_clock); /* Signal handling */ sigfillset(&set); sigprocmask(SIG_BLOCK, &set, NULL); trap_init(argv[0]); /* Initialise configuration */ uuid_init(); idnode_init(); config_init(opt_config); /** * Initialize subsystems */ api_init(); fsmonitor_init(); #if ENABLE_LIBAV libav_init(); transcoding_init(); #endif imagecache_init(); service_init(); #if ENABLE_TSFILE if(opt_tsfile.num) { tsfile_init(opt_tsfile_tuner ?: opt_tsfile.num); for (i = 0; i < opt_tsfile.num; i++) tsfile_add_file(opt_tsfile.str[i]); } #endif #if ENABLE_MPEGTS_DVB dvb_network_init(); #endif #if ENABLE_IPTV iptv_init(); #endif #if ENABLE_LINUXDVB linuxdvb_init(adapter_mask); #endif channel_init(); subscription_init(); access_init(opt_firstrun, opt_noacl); #if ENABLE_TIMESHIFT timeshift_init(); #endif http_client_init(); tcp_server_init(opt_ipv6); http_server_init(opt_bindaddr); webui_init(); service_mapper_init(); descrambler_init(); epggrab_init(); epg_init(); dvr_init(); htsp_init(opt_bindaddr); if(opt_subscribe != NULL) subscription_dummy_join(opt_subscribe, 1); avahi_init(); epg_updated(); // cleanup now all prev ref's should have been created pthread_mutex_unlock(&global_lock); /** * Wait for SIGTERM / SIGINT, but only in this thread */ sigemptyset(&set); sigaddset(&set, SIGTERM); sigaddset(&set, SIGINT); signal(SIGTERM, doexit); signal(SIGINT, doexit); pthread_sigmask(SIG_UNBLOCK, &set, NULL); tvhlog(LOG_NOTICE, "START", "HTS Tvheadend version %s started, " "running as PID:%d UID:%d GID:%d, CWD:%s CNF:%s", tvheadend_version, getpid(), getuid(), getgid(), getcwd(buf, sizeof(buf)), hts_settings_get_root()); if(opt_abort) abort(); mainloop(); tvhftrace("main", htsp_done); tvhftrace("main", http_server_done); tvhftrace("main", webui_done); tvhftrace("main", http_client_done); tvhftrace("main", fsmonitor_done); #if ENABLE_MPEGTS_DVB tvhftrace("main", dvb_network_done); #endif #if ENABLE_IPTV tvhftrace("main", iptv_done); #endif #if ENABLE_LINUXDVB tvhftrace("main", linuxdvb_done); #endif #if ENABLE_TSFILE tvhftrace("main", tsfile_done); #endif // Note: the locking is obviously a bit redundant, but without // we need to disable the gtimer_arm call in epg_save() pthread_mutex_lock(&global_lock); tvhftrace("main", epg_save); #if ENABLE_TIMESHIFT tvhftrace("main", timeshift_term); #endif pthread_mutex_unlock(&global_lock); tvhftrace("main", epggrab_done); tvhftrace("main", tcp_server_done); tvhftrace("main", descrambler_done); tvhftrace("main", service_mapper_done); tvhftrace("main", service_done); tvhftrace("main", channel_done); tvhftrace("main", dvr_done); tvhftrace("main", subscription_done); tvhftrace("main", access_done); tvhftrace("main", epg_done); tvhftrace("main", avahi_done); tvhftrace("main", imagecache_done); tvhftrace("main", idnode_done); tvhftrace("main", lang_code_done); tvhftrace("main", api_done); tvhftrace("main", config_done); tvhftrace("main", hts_settings_done); tvhftrace("main", dvb_done); tvhftrace("main", lang_str_done); tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); tvhlog_end(); if(opt_fork) unlink(opt_pidpath); free(opt_tsfile.str); return 0; }
int h264_decode_slice_header(elementary_stream_t *st, bitstream_t *bs, int *pkttype, int *isfield) { h264_private_t *p; h264_pps_t *pps; h264_sps_t *sps; uint32_t slice_type, pps_id, width, height, v; uint64_t d; *pkttype = 0; *isfield = 0; if ((p = st->es_priv) == NULL) return -1; read_golomb_ue(bs); /* first_mb_in_slice */ slice_type = read_golomb_ue(bs); if (slice_type > 4) slice_type -= 5; /* Fixed slice type per frame */ pps_id = read_golomb_ue(bs); if (pps_id >= MAX_PPS_COUNT) return -1; pps = &p->pps[pps_id]; if (!pps->valid) return -1; sps = &p->sps[pps->sps_id]; if (!sps->valid) return -1; if (!sps->max_frame_num_bits) return -1; switch(slice_type) { case 0: *pkttype = PKT_P_FRAME; break; case 1: *pkttype = PKT_B_FRAME; break; case 2: *pkttype = PKT_I_FRAME; break; default: return -1; } skip_bits(bs, sps->max_frame_num_bits); if (!sps->mbs_only_flag) if (read_bits1(bs)) { skip_bits1(bs); // bottom field *isfield = 1; } d = 0; if (sps->time_scale) d = 180000 * (uint64_t)sps->units_in_tick / (uint64_t)sps->time_scale; if (d == 0 && st->es_frame_duration == 0 && p->start + 4 < dispatch_clock) { tvhwarn("parser", "H264 stream has not timing information, using 30fps"); d = 3000; /* 90000/30 = 3000 : 30fps */ } if (sps->cbpsize) st->es_vbv_size = sps->cbpsize; st->es_vbv_delay = -1; width = sps->width; height = sps->height * (2 - sps->mbs_only_flag); if (width && height && d) parser_set_stream_vparam(st, width, height, d); if (sps->aspect_num && sps->aspect_den) { width *= sps->aspect_num; height *= sps->aspect_den; if (width && height) { v = gcdU32(width, height); st->es_aspect_num = width / v; st->es_aspect_den = height / v; } } else { st->es_aspect_num = 0; st->es_aspect_den = 1; } return 0; }
udp_connection_t * udp_bind ( const char *subsystem, const char *name, const char *bindaddr, int port, const char *ifname, int rxsize ) { int fd, ifindex, reuse = 1; udp_connection_t *uc; char buf[256]; socklen_t addrlen; uc = calloc(1, sizeof(udp_connection_t)); uc->fd = -1; uc->host = bindaddr ? strdup(bindaddr) : NULL; uc->port = port; uc->ifname = ifname ? strdup(ifname) : NULL; uc->subsystem = subsystem ? strdup(subsystem) : NULL; uc->name = name ? strdup(name) : NULL; uc->rxtxsize = rxsize; if (udp_resolve(uc, 1) < 0) { udp_close(uc); return UDP_FATAL_ERROR; } /* Open socket */ if ((fd = tvh_socket(uc->ip.ss_family, SOCK_DGRAM, 0)) == -1) { tvherror(subsystem, "%s - failed to create socket [%s]", name, strerror(errno)); udp_close(uc); return UDP_FATAL_ERROR; } /* Mark reuse address */ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); /* Bind to interface */ ifindex = udp_ifindex_required(uc) ? udp_get_ifindex(ifname) : 0; if (ifindex < 0) { tvherror(subsystem, "%s - could not find interface %s", name, ifname); goto error; } /* IPv4 */ if (uc->ip.ss_family == AF_INET) { #if defined(PLATFORM_DARWIN) struct ip_mreq m; #else struct ip_mreqn m; #endif memset(&m, 0, sizeof(m)); /* Bind */ if (bind(fd, (struct sockaddr *)&uc->ip, sizeof(struct sockaddr_in)) == -1) { inet_ntop(AF_INET, &IP_AS_V4(uc->ip, addr), buf, sizeof(buf)); tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", name, buf, ntohs(IP_AS_V4(uc->ip, port)), strerror(errno)); goto error; } if (uc->multicast) { /* Join group */ m.imr_multiaddr = IP_AS_V4(uc->ip, addr); #if !defined(PLATFORM_DARWIN) m.imr_address.s_addr = 0; m.imr_ifindex = ifindex; #else if (udp_get_ifaddr(fd, ifname, &m.imr_interface) == -1) { tvherror(subsystem, "%s - cannot find ip address for interface %s [e=%s]", name, ifname, strerror(errno)); goto error; } #endif if (setsockopt(fd, udp_get_solip(), IP_ADD_MEMBERSHIP, &m, sizeof(m))) { inet_ntop(AF_INET, &m.imr_multiaddr, buf, sizeof(buf)); tvhwarn(subsystem, "%s - cannot join %s [%s]", name, buf, strerror(errno)); } } /* Bind to IPv6 group */ } else { struct ipv6_mreq m; memset(&m, 0, sizeof(m)); /* Bind */ if (bind(fd, (struct sockaddr *)&uc->ip, sizeof(struct sockaddr_in6)) == -1) { inet_ntop(AF_INET6, &IP_AS_V6(uc->ip, addr), buf, sizeof(buf)); tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", name, buf, ntohs(IP_AS_V6(uc->ip, port)), strerror(errno)); goto error; } if (uc->multicast) { /* Join group */ m.ipv6mr_multiaddr = IP_AS_V6(uc->ip, addr); m.ipv6mr_interface = ifindex; #ifdef SOL_IPV6 if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m, sizeof(m))) { inet_ntop(AF_INET, &m.ipv6mr_multiaddr, buf, sizeof(buf)); tvhwarn(subsystem, "%s - cannot join %s [%s]", name, buf, strerror(errno)); } #else tvherror(subsystem, "IPv6 multicast not supported"); goto error; #endif } } addrlen = sizeof(uc->ip); getsockname(fd, (struct sockaddr *)&uc->ip, &addrlen); /* Increase RX buffer size */ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rxsize, sizeof(rxsize)) == -1) tvhwarn(subsystem, "%s - cannot increase UDP rx buffer size [%s]", name, strerror(errno)); uc->fd = fd; return uc; error: udp_close(uc); return NULL; }
/** * Write a packet to the muxer */ static int lav_muxer_write_pkt(muxer_t *m, streaming_message_type_t smt, void *data) { int i; AVFormatContext *oc; AVStream *st; AVPacket packet; enum AVCodecID codec_id; th_pkt_t *pkt = (th_pkt_t*)data, *opkt; lav_muxer_t *lm = (lav_muxer_t*)m; unsigned char *tofree; int rc = 0; assert(smt == SMT_PACKET); oc = lm->lm_oc; if(!oc->nb_streams) { tvherror(LS_LIBAV, "No streams to mux"); rc = -1; goto ret; } if(!lm->lm_init) { tvherror(LS_LIBAV, "Muxer not initialized correctly"); rc = -1; goto ret; } for(i=0; i<oc->nb_streams; i++) { st = oc->streams[i]; if(st->id != pkt->pkt_componentindex) continue; if(pkt->pkt_payload == NULL) continue; tofree = NULL; av_init_packet(&packet); codec_id = st->codec->codec_id; if((lm->lm_h264_filter && codec_id == AV_CODEC_ID_H264) || (lm->lm_hevc_filter && codec_id == AV_CODEC_ID_HEVC)) { pkt = avc_convert_pkt(opkt = pkt); pkt_ref_dec(opkt); if(av_bitstream_filter_filter(st->codec->codec_id == AV_CODEC_ID_H264 ? lm->lm_h264_filter : lm->lm_hevc_filter, st->codec, NULL, &packet.data, &packet.size, pktbuf_ptr(pkt->pkt_payload), pktbuf_len(pkt->pkt_payload), pkt->pkt_frametype < PKT_P_FRAME) < 0) { tvhwarn(LS_LIBAV, "Failed to filter bitstream"); if (packet.data != pktbuf_ptr(pkt->pkt_payload)) av_free(packet.data); break; } else { tofree = packet.data; } } else if (codec_id == AV_CODEC_ID_AAC) { /* remove ADTS header */ packet.data = pktbuf_ptr(pkt->pkt_payload) + 7; packet.size = pktbuf_len(pkt->pkt_payload) - 7; } else { if (lm->m_config.m_type == MC_AVMP4 && (codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_HEVC)) { pkt = avc_convert_pkt(opkt = pkt); pkt_ref_dec(opkt); } packet.data = pktbuf_ptr(pkt->pkt_payload); packet.size = pktbuf_len(pkt->pkt_payload); } packet.stream_index = st->index; packet.pts = av_rescale_q(pkt->pkt_pts , mpeg_tc, st->time_base); packet.dts = av_rescale_q(pkt->pkt_dts , mpeg_tc, st->time_base); packet.duration = av_rescale_q(pkt->pkt_duration, mpeg_tc, st->time_base); if(pkt->pkt_frametype < PKT_P_FRAME) packet.flags |= AV_PKT_FLAG_KEY; if((rc = av_interleaved_write_frame(oc, &packet))) tvhwarn(LS_LIBAV, "Failed to write frame"); if(tofree && tofree != pktbuf_ptr(pkt->pkt_payload)) av_free(tofree); break; } ret: lm->m_errors += (rc != 0); pkt_ref_dec(pkt); return rc; }
/** * Init the muxer with streams */ static int lav_muxer_init(muxer_t* m, struct streaming_start *ss, const char *name) { int i; streaming_start_component_t *ssc; AVFormatContext *oc; AVDictionary *opts = NULL; lav_muxer_t *lm = (lav_muxer_t*)m; char app[128]; snprintf(app, sizeof(app), "Tvheadend %s", tvheadend_version); oc = lm->lm_oc; av_dict_set(&oc->metadata, "title", name, 0); av_dict_set(&oc->metadata, "service_name", name, 0); av_dict_set(&oc->metadata, "service_provider", app, 0); if(lm->m_config.m_type == MC_MPEGTS) { lm->lm_h264_filter = av_bitstream_filter_init("h264_mp4toannexb"); lm->lm_hevc_filter = av_bitstream_filter_init("hevc_mp4toannexb"); } oc->max_delay = 0.7 * AV_TIME_BASE; for(i=0; i < ss->ss_num_components; i++) { ssc = &ss->ss_components[i]; if(ssc->ssc_disabled) continue; if(!lav_muxer_support_stream(lm->m_config.m_type, ssc->ssc_type)) { tvhwarn(LS_LIBAV, "%s is not supported in %s", streaming_component_type2txt(ssc->ssc_type), muxer_container_type2txt(lm->m_config.m_type)); ssc->ssc_muxer_disabled = 1; continue; } if(lav_muxer_add_stream(lm, ssc)) { tvherror(LS_LIBAV, "Failed to add %s stream", streaming_component_type2txt(ssc->ssc_type)); ssc->ssc_muxer_disabled = 1; continue; } } if(lm->m_config.m_type == MC_AVMP4) { av_dict_set(&opts, "frag_duration", "1", 0); av_dict_set(&opts, "ism_lookahead", "0", 0); } if(!lm->lm_oc->nb_streams) { tvherror(LS_LIBAV, "No supported streams available"); lm->m_errors++; return -1; } else if(avformat_write_header(lm->lm_oc, &opts) < 0) { tvherror(LS_LIBAV, "Failed to write %s header", muxer_container_type2txt(lm->m_config.m_type)); lm->m_errors++; return -1; } if (opts) av_dict_free(&opts); lm->lm_init = 1; return 0; }