| /* |
| * Copyright (c) 2011-2012 - Mauro Carvalho Chehab |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation version 2 |
| * of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
| * |
| * Based on dvb-apps tzap utility, made by: |
| * Bernard Hatt 24/2/04 |
| */ |
| |
| #define _FILE_OFFSET_BITS 64 |
| #define _LARGEFILE_SOURCE 1 |
| #define _LARGEFILE64_SOURCE 1 |
| |
| /* |
| * Use a buffer big enough at least 1 second of data. It is interesting |
| * To have it multiple of a page. So, define it as a multiply of |
| * 4096. |
| */ |
| #define DVB_BUF_SIZE (4096 * 8 * 188) |
| |
| /* |
| * Size of the buffer on read operations. The better is if it is |
| * smaller than DVB_BUF_SIZE, as we want to give more time for |
| * write() syscalls to be able to flush data. |
| */ |
| #define BUFLEN (188 * 512) |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <signal.h> |
| #include <argp.h> |
| #include <sys/time.h> |
| #include <time.h> |
| |
| #include <config.h> |
| |
| #ifdef ENABLE_NLS |
| # define _(string) gettext(string) |
| # include "gettext.h" |
| # include <locale.h> |
| # include <langinfo.h> |
| # include <iconv.h> |
| #else |
| # define _(string) string |
| #endif |
| |
| # define N_(string) string |
| |
| #include <linux/dvb/dmx.h> |
| #include "libdvbv5/dvb-file.h" |
| #include "libdvbv5/dvb-demux.h" |
| #include "libdvbv5/dvb-dev.h" |
| #include "libdvbv5/dvb-scan.h" |
| #include "libdvbv5/header.h" |
| #include "libdvbv5/countries.h" |
| |
| #define CHANNEL_FILE "channels.conf" |
| #define PROGRAM_NAME "dvbv5-zap" |
| |
| |
| #ifndef O_LARGEFILE |
| # define O_LARGEFILE 0 |
| #endif |
| |
| |
| const int NANO_SECONDS_IN_SEC = 1000000000; |
| |
| const char *argp_program_version = PROGRAM_NAME " version " V4L_UTILS_VERSION; |
| const char *argp_program_bug_address = "Mauro Carvalho Chehab <mchehab@kernel.org>"; |
| |
| struct arguments { |
| char *confname, *lnb_name, *output, *demux_dev, *dvr_dev, *dvr_fname; |
| char *filename, *dvr_pipe; |
| unsigned adapter, frontend, demux, get_detected, get_nit; |
| int lna, lnb, sat_number; |
| unsigned diseqc_wait, silent, verbose, frontend_only, freq_bpf; |
| unsigned timeout, dvr, rec_psi, exit_after_tuning; |
| unsigned n_apid, n_vpid, all_pids; |
| enum dvb_file_formats input_format, output_format; |
| unsigned traffic_monitor, low_traffic, non_human, port; |
| char *search, *server; |
| const char *cc; |
| |
| /* Used by status print */ |
| unsigned n_status_lines; |
| }; |
| |
| static const struct argp_option options[] = { |
| {"adapter", 'a', N_("adapter#"), 0, N_("use given adapter (default 0)"), 0}, |
| {"audio_pid", 'A', N_("audio_pid#"), 0, N_("audio pid program to use (default 0)"), 0}, |
| {"channels", 'c', N_("file"), 0, N_("read channels list from 'file'"), 0}, |
| {"demux", 'd', N_("demux#"), 0, N_("use given demux (default 0)"), 0}, |
| {"frontend", 'f', N_("frontend#"), 0, N_("use given frontend (default 0)"), 0}, |
| {"input-format", 'I', N_("format"), 0, N_("Input format: ZAP, CHANNEL, DVBV5 (default: DVBV5)"), 0}, |
| {"lna", 'w', N_("LNA (0, 1, -1)"), 0, N_("enable/disable/auto LNA power"), 0}, |
| {"lnbf", 'l', N_("LNBf_type"), 0, N_("type of LNBf to use. 'help' lists the available ones"), 0}, |
| {"search", 'L', N_("string"), 0, N_("search/look for a string inside the traffic"), 0}, |
| {"monitor", 'm', NULL, 0, N_("monitors the DVB traffic"), 0}, |
| {"output", 'o', N_("file"), 0, N_("output filename (use -o - for stdout)"), 0}, |
| {"pat", 'p', NULL, 0, N_("add pat and pmt to TS recording (implies -r)"), 0}, |
| {"all-pids", 'P', NULL, 0, N_("don't filter any pids. Instead, outputs all of them"), 0 }, |
| {"record", 'r', NULL, 0, N_("set up /dev/dvb/adapterX/dvr0 for TS recording"), 0}, |
| {"silence", 's', NULL, 0, N_("increases silence (can be used more than once)"), 0}, |
| {"sat_number", 'S', N_("satellite_number"), 0, N_("satellite number. If not specified, disable DISEqC"), 0}, |
| {"timeout", 't', N_("seconds"), 0, N_("timeout for zapping and for recording"), 0}, |
| {"freq_bpf", 'U', N_("frequency"), 0, N_("SCR/Unicable band-pass filter frequency to use, in kHz"), 0}, |
| {"verbose", 'v', NULL, 0, N_("verbose debug messages (can be used more than once)"), 0}, |
| {"video_pid", 'V', N_("video_pid#"), 0, N_("video pid program to use (default 0)"), 0}, |
| {"wait", 'W', N_("time"), 0, N_("adds additional wait time for DISEqC command completion"), 0}, |
| {"exit", 'x', NULL, 0, N_("exit after tuning"), 0}, |
| {"low_traffic", 'X', N_("packets_per_sec"), 0, N_("sets DVB low traffic threshold. PIDs with less than this amount of packets per second will be ignored. Default: 1 packet per second"), 0}, |
| {"cc", 'C', N_("country_code"), 0, N_("Set the default country to be used (in ISO 3166-1 two letter code)"), 0}, |
| {"non-numan", 'N', NULL, 0, N_("Non-human formatted stats (useful for scripts)"), 0}, |
| {"server", 'H', N_("SERVER"), 0, N_("dvbv5-daemon host IP address"), 0}, |
| {"tcp-port", 'T', N_("PORT"), 0, N_("dvbv5-daemon host tcp port"), 0}, |
| {"dvr-pipe", 'D', N_("PIPE"), 0, N_("Named pipe for DVR output, when using remote access (by default: /tmp/dvr-pipe)"), 0}, |
| {"help", '?', 0, 0, N_("Give this help list"), -1}, |
| {"usage", -3, 0, 0, N_("Give a short usage message")}, |
| {"version", -4, 0, 0, N_("Print program version"), -1}, |
| { 0, 0, 0, 0, 0, 0 } |
| }; |
| |
| static int timeout_flag = 0; |
| |
| #define ERROR(x...) \ |
| do { \ |
| fprintf(stderr, _("ERROR: ")); \ |
| fprintf(stderr, x); \ |
| fprintf(stderr, "\n"); \ |
| } while (0) |
| |
| #define PERROR(x...) \ |
| do { \ |
| fprintf(stderr, _("ERROR: ")); \ |
| fprintf(stderr, x); \ |
| fprintf(stderr, " (%s)\n", strerror(errno)); \ |
| } while (0) |
| |
| #define monitor_log(msg, args...) \ |
| do { \ |
| struct timespec __now = { 0 }; \ |
| float __diff; \ |
| \ |
| clock_gettime(CLOCK_MONOTONIC, &__now); \ |
| __diff = __now.tv_sec * 1. \ |
| + __now.tv_nsec *1. / NANO_SECONDS_IN_SEC; \ |
| fprintf(stderr, msg, __diff, ##args); \ |
| } while (0) |
| |
| |
| static int parse(struct arguments *args, |
| struct dvb_v5_fe_parms *parms, |
| char *channel, |
| int *vpid, int *apid, int *sid) |
| { |
| struct dvb_file *dvb_file; |
| struct dvb_entry *entry; |
| int i; |
| uint32_t sys; |
| |
| /* This is used only when reading old formats */ |
| switch (parms->current_sys) { |
| case SYS_DVBT: |
| case SYS_DVBS: |
| case SYS_DVBC_ANNEX_A: |
| case SYS_ATSC: |
| sys = parms->current_sys; |
| break; |
| case SYS_DVBC_ANNEX_C: |
| sys = SYS_DVBC_ANNEX_A; |
| break; |
| case SYS_DVBC_ANNEX_B: |
| sys = SYS_ATSC; |
| break; |
| case SYS_ISDBT: |
| case SYS_DTMB: |
| sys = SYS_DVBT; |
| break; |
| default: |
| sys = SYS_UNDEFINED; |
| break; |
| } |
| dvb_file = dvb_read_file_format(args->confname, sys, |
| args->input_format); |
| if (!dvb_file) |
| return -2; |
| |
| for (entry = dvb_file->first_entry; entry != NULL; entry = entry->next) { |
| if (entry->channel && !strcmp(entry->channel, channel)) |
| break; |
| if (entry->vchannel && !strcmp(entry->vchannel, channel)) |
| break; |
| } |
| /* |
| * Give a second shot, using a case insensitive seek |
| */ |
| if (!entry) { |
| for (entry = dvb_file->first_entry; entry != NULL; |
| entry = entry->next) { |
| if (entry->channel && !strcasecmp(entry->channel, channel)) |
| break; |
| } |
| } |
| |
| /* |
| * When this tool is used to just tune to a channel, to monitor it or |
| * to capture all PIDs, all it needs is a frequency. |
| * So, let the tool to accept a frequency as the tuning channel on those |
| * cases. |
| * This way, a file in "channel" format can be used instead of a zap file. |
| * It is also easier to use it for testing purposes. |
| */ |
| if (!entry && (!args->dvr && !args->rec_psi)) { |
| uint32_t f, freq = atoi(channel); |
| if (freq) { |
| for (entry = dvb_file->first_entry; entry != NULL; |
| entry = entry->next) { |
| dvb_retrieve_entry_prop(entry, DTV_FREQUENCY, &f); |
| if (f == freq) |
| break; |
| } |
| |
| } |
| } |
| |
| if (!entry) { |
| ERROR("Can't find channel"); |
| dvb_file_free(dvb_file); |
| return -3; |
| } |
| |
| /* |
| * Both the DVBv5 format and the command line parameters may |
| * specify the LNBf. If both have the definition, use the one |
| * provided by the command line parameter, overriding the one |
| * stored in the channel file. |
| */ |
| if (entry->lnb && !parms->lnb) { |
| int lnb = dvb_sat_search_lnb(entry->lnb); |
| if (lnb == -1) { |
| ERROR("unknown LNB %s\n", entry->lnb); |
| dvb_file_free(dvb_file); |
| return -1; |
| } |
| parms->lnb = dvb_sat_get_lnb(lnb); |
| } |
| |
| if (parms->sat_number < 0 && entry->sat_number >= 0) |
| parms->sat_number = entry->sat_number; |
| |
| if (entry->video_pid) { |
| if (args->n_vpid < entry->video_pid_len) |
| *vpid = entry->video_pid[args->n_vpid]; |
| else |
| *vpid = entry->video_pid[0]; |
| } |
| if (entry->audio_pid) { |
| if (args->n_apid < entry->audio_pid_len) |
| *apid = entry->audio_pid[args->n_apid]; |
| else |
| *apid = entry->audio_pid[0]; |
| } |
| if (entry->other_el_pid) { |
| int i, type = -1; |
| for (i = 0; i < entry->other_el_pid_len; i++) { |
| if (type != entry->other_el_pid[i].type) { |
| type = entry->other_el_pid[i].type; |
| if (i) |
| fprintf(stderr, "\n"); |
| fprintf(stderr, _("service has pid type %02x: "), type); |
| } |
| fprintf(stderr, " %d", entry->other_el_pid[i].pid); |
| } |
| fprintf(stderr, "\n"); |
| } |
| *sid = entry->service_id; |
| |
| /* First of all, set the delivery system */ |
| dvb_retrieve_entry_prop(entry, DTV_DELIVERY_SYSTEM, &sys); |
| dvb_set_compat_delivery_system(parms, sys); |
| |
| /* Copy data into parms */ |
| for (i = 0; i < entry->n_props; i++) { |
| uint32_t data = entry->props[i].u.data; |
| /* Don't change the delivery system */ |
| if (entry->props[i].cmd == DTV_DELIVERY_SYSTEM) |
| continue; |
| dvb_fe_store_parm(parms, entry->props[i].cmd, data); |
| if (parms->current_sys == SYS_ISDBT) { |
| dvb_fe_store_parm(parms, DTV_ISDBT_PARTIAL_RECEPTION, 0); |
| dvb_fe_store_parm(parms, DTV_ISDBT_SOUND_BROADCASTING, 0); |
| dvb_fe_store_parm(parms, DTV_ISDBT_LAYER_ENABLED, 0x07); |
| if (entry->props[i].cmd == DTV_CODE_RATE_HP) { |
| dvb_fe_store_parm(parms, DTV_ISDBT_LAYERA_FEC, |
| data); |
| dvb_fe_store_parm(parms, DTV_ISDBT_LAYERB_FEC, |
| data); |
| dvb_fe_store_parm(parms, DTV_ISDBT_LAYERC_FEC, |
| data); |
| } else if (entry->props[i].cmd == DTV_MODULATION) { |
| dvb_fe_store_parm(parms, |
| DTV_ISDBT_LAYERA_MODULATION, |
| data); |
| dvb_fe_store_parm(parms, |
| DTV_ISDBT_LAYERB_MODULATION, |
| data); |
| dvb_fe_store_parm(parms, |
| DTV_ISDBT_LAYERC_MODULATION, |
| data); |
| } |
| } |
| if (parms->current_sys == SYS_ATSC && |
| entry->props[i].cmd == DTV_MODULATION) { |
| if (data != VSB_8 && data != VSB_16) |
| dvb_fe_store_parm(parms, |
| DTV_DELIVERY_SYSTEM, |
| SYS_DVBC_ANNEX_B); |
| } |
| } |
| |
| dvb_file_free(dvb_file); |
| return 0; |
| } |
| |
| static int setup_frontend(struct arguments *args, |
| struct dvb_v5_fe_parms *parms) |
| { |
| int rc; |
| uint32_t freq; |
| |
| if (args->silent < 2) { |
| rc = dvb_fe_retrieve_parm(parms, DTV_FREQUENCY, &freq); |
| if (rc < 0) { |
| ERROR("can't get the frequency"); |
| return -1; |
| } |
| fprintf(stderr, _("tuning to %i Hz\n"), freq); |
| } |
| |
| rc = dvb_fe_set_parms(parms); |
| if (rc < 0) { |
| ERROR("dvb_fe_set_parms failed"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static void do_timeout(int x) |
| { |
| (void)x; |
| |
| if (timeout_flag == 0) { |
| timeout_flag = 1; |
| alarm(2); |
| signal(SIGALRM, do_timeout); |
| } else { |
| /* something has gone wrong ... exit */ |
| fprintf(stderr, "Forcing program stop due to timeout or terminate signal\n"); |
| exit(1); |
| } |
| } |
| |
| static int print_non_human_stats(FILE *fd, struct dvb_v5_fe_parms *parms) |
| { |
| int rc; |
| fe_status_t status; |
| uint32_t snr = 0, _signal = 0, quality = 0; |
| uint32_t ber = 0, per = 0, pre_ber = 0, uncorrected_blocks = 0; |
| |
| rc = dvb_fe_get_stats(parms); |
| if (rc < 0) { |
| PERROR("dvb_fe_get_stats failed"); |
| return -1; |
| } |
| |
| dvb_fe_retrieve_stats(parms, DTV_STATUS, &status); |
| dvb_fe_retrieve_stats(parms, DTV_QUALITY, &quality); |
| dvb_fe_retrieve_stats(parms, DTV_STAT_SIGNAL_STRENGTH, &_signal); |
| dvb_fe_retrieve_stats(parms, DTV_STAT_CNR, &snr); |
| dvb_fe_retrieve_stats(parms, DTV_BER, &ber); |
| dvb_fe_retrieve_stats(parms, DTV_STAT_ERROR_BLOCK_COUNT, &uncorrected_blocks); |
| dvb_fe_retrieve_stats(parms, DTV_PRE_BER, &pre_ber); |
| dvb_fe_retrieve_stats(parms, DTV_PER, &per); |
| |
| fprintf(fd,"status %02x | quality %02x | signal %04x | snr %04x | ber %08x | unc %08x | pre_ber %08x | per %08x | ", |
| status, quality, _signal, snr, ber, uncorrected_blocks, pre_ber, per); |
| |
| if (status & FE_HAS_LOCK) |
| fprintf(fd, "FE_HAS_LOCK"); |
| |
| fprintf(fd, "\n"); |
| fflush(fd); |
| |
| return 0; |
| } |
| |
| static int print_frontend_stats(FILE *fd, |
| struct arguments *args, |
| struct dvb_v5_fe_parms *parms) |
| { |
| char buf[512], *p; |
| int rc, i, len, show; |
| uint32_t status = 0; |
| |
| if (args->non_human) |
| return print_non_human_stats(fd, parms); |
| |
| /* Move cursor up and cleans down */ |
| if (isatty(fileno(fd)) && args->n_status_lines) |
| fprintf(fd, "\r\x1b[%dA\x1b[J", args->n_status_lines); |
| |
| args->n_status_lines = 0; |
| |
| rc = dvb_fe_get_stats(parms); |
| if (rc) { |
| ERROR("dvb_fe_get_stats failed"); |
| return -1; |
| } |
| |
| p = buf; |
| len = sizeof(buf); |
| dvb_fe_snprintf_stat(parms, DTV_STATUS, NULL, 0, &p, &len, &show); |
| |
| for (i = 0; i < MAX_DTV_STATS; i++) { |
| show = 1; |
| |
| dvb_fe_snprintf_stat(parms, DTV_QUALITY, _("Quality"), |
| i, &p, &len, &show); |
| |
| dvb_fe_snprintf_stat(parms, DTV_STAT_SIGNAL_STRENGTH, _("Signal"), |
| i, &p, &len, &show); |
| |
| dvb_fe_snprintf_stat(parms, DTV_STAT_CNR, _("C/N"), |
| i, &p, &len, &show); |
| |
| dvb_fe_snprintf_stat(parms, DTV_STAT_ERROR_BLOCK_COUNT, _("UCB"), |
| i, &p, &len, &show); |
| |
| dvb_fe_snprintf_stat(parms, DTV_BER, _("postBER"), |
| i, &p, &len, &show); |
| |
| dvb_fe_snprintf_stat(parms, DTV_PRE_BER, _("preBER"), |
| i, &p, &len, &show); |
| |
| dvb_fe_snprintf_stat(parms, DTV_PER, _("PER"), |
| i, &p, &len, &show); |
| |
| if (p != buf) { |
| if (args->n_status_lines) |
| fprintf(fd, "\t%s\n", buf); |
| else |
| fprintf(fd, "%s\n", buf); |
| |
| args->n_status_lines++; |
| |
| p = buf; |
| len = sizeof(buf); |
| } |
| } |
| |
| fflush(fd); |
| |
| /* While not lock, display status on a new line */ |
| dvb_fe_retrieve_stats(parms, DTV_STATUS, &status); |
| if (!isatty(fileno(fd)) || !(status & FE_HAS_LOCK)) |
| fprintf(fd, "\n"); |
| |
| return 0; |
| } |
| |
| static int check_frontend(struct arguments *args, |
| struct dvb_v5_fe_parms *parms) |
| { |
| int rc; |
| fe_status_t status = 0; |
| do { |
| rc = dvb_fe_get_stats(parms); |
| if (rc) { |
| ERROR("dvb_fe_get_stats failed"); |
| usleep(1000000); |
| continue; |
| } |
| |
| status = 0; |
| rc = dvb_fe_retrieve_stats(parms, DTV_STATUS, &status); |
| if (rc) { |
| ERROR("dvb_fe_retrieve_stats failed"); |
| usleep(1000000); |
| continue; |
| } |
| if (!args->silent) |
| print_frontend_stats(stderr, args, parms); |
| if (status & FE_HAS_LOCK) |
| break; |
| usleep(1000000); |
| } while (!timeout_flag); |
| if (args->silent < 2) |
| print_frontend_stats(stderr, args, parms); |
| |
| return status & FE_HAS_LOCK; |
| } |
| |
| static void get_show_stats(FILE *fp, struct arguments *args, |
| struct dvb_v5_fe_parms *parms, |
| int loop) |
| { |
| int rc; |
| |
| args->n_status_lines = 0; |
| do { |
| rc = dvb_fe_get_stats(parms); |
| if (!rc) |
| print_frontend_stats(fp, args, parms); |
| if (!timeout_flag && loop) |
| usleep(1000000); |
| } while (!timeout_flag && loop); |
| } |
| |
| static struct timespec *elapsed_time(struct timespec *start) |
| { |
| static struct timespec elapsed; |
| struct timespec end; |
| |
| if (!start->tv_sec && !start->tv_nsec) |
| return NULL; |
| |
| if (clock_gettime(CLOCK_MONOTONIC, &end)) |
| return NULL; |
| |
| elapsed.tv_sec = end.tv_sec - start->tv_sec; |
| elapsed.tv_nsec = end.tv_nsec - start->tv_nsec; |
| if (elapsed.tv_nsec < 0) { |
| elapsed.tv_sec--; |
| elapsed.tv_nsec += NANO_SECONDS_IN_SEC; |
| } |
| return &elapsed; |
| } |
| |
| static void copy_to_file(struct dvb_open_descriptor *in_fd, int out_fd, |
| int timeout, int silent) |
| { |
| char buf[BUFLEN]; |
| int r, first = 1; |
| long long int rc = 0LL; |
| struct timespec start, *elapsed; |
| |
| /* Initialize start time, due to -EOVERFLOW with first == 1 */ |
| clock_gettime(CLOCK_MONOTONIC, &start); |
| |
| while (timeout_flag == 0) { |
| r = dvb_dev_read(in_fd, buf, sizeof(buf)); |
| if (r < 0) { |
| if (r == -EOVERFLOW) { |
| elapsed = elapsed_time(&start); |
| if (!elapsed) |
| fprintf(stderr, _("buffer overrun at %lld\n"), rc); |
| else |
| fprintf(stderr, _("buffer overrun after %lld.%02ld seconds\n"), |
| (long long)elapsed->tv_sec, |
| elapsed->tv_nsec / 10000000); |
| continue; |
| } |
| ERROR("Read failed"); |
| break; |
| } |
| |
| /* |
| * It takes a while for a DVB device to start streaming, as the |
| * hardware may be waiting for some locks. The safest way to |
| * ensure that a program record will have the start amount of |
| * time specified by the user is to restart the timeout alarm |
| * here, after the first succeded read. |
| * |
| * So, let's reset the start time here. |
| */ |
| if (first) { |
| if (timeout > 0) |
| alarm(timeout); |
| |
| clock_gettime(CLOCK_MONOTONIC, &start); |
| first = 0; |
| } |
| |
| if (write(out_fd, buf, r) < 0) { |
| PERROR(_("Write failed")); |
| break; |
| } |
| |
| rc += r; |
| } |
| if (silent < 2) { |
| if (timeout) |
| fprintf(stderr, _("received %lld bytes (%lld Kbytes/sec)\n"), rc, |
| rc / (1024 * timeout)); |
| else |
| fprintf(stderr, _("received %lld bytes\n"), rc); |
| } |
| } |
| |
| static error_t parse_opt(int k, char *optarg, struct argp_state *state) |
| { |
| struct arguments *args = state->input; |
| |
| switch (k) { |
| case 'a': |
| args->adapter = strtoul(optarg, NULL, 0); |
| break; |
| case 'f': |
| args->frontend = strtoul(optarg, NULL, 0); |
| break; |
| case 'd': |
| args->demux = strtoul(optarg, NULL, 0); |
| break; |
| case 't': |
| args->timeout = strtoul(optarg, NULL, 0); |
| break; |
| case 'I': |
| args->input_format = dvb_parse_format(optarg); |
| break; |
| case 'o': |
| args->filename = strdup(optarg); |
| /* fall through */ |
| case 'r': |
| args->dvr = 1; |
| break; |
| case 'p': |
| args->rec_psi = 1; |
| break; |
| case 'x': |
| args->exit_after_tuning = 1; |
| break; |
| case 'c': |
| args->confname = strdup(optarg); |
| break; |
| case 'w': |
| if (!strcasecmp(optarg,"on")) { |
| args->lna = 1; |
| } else if (!strcasecmp(optarg,"off")) { |
| args->lna = 0; |
| } else if (!strcasecmp(optarg,"auto")) { |
| args->lna = LNA_AUTO; |
| } else { |
| int val = strtoul(optarg, NULL, 0); |
| if (!val) |
| args->lna = 0; |
| else if (val > 0) |
| args->lna = 1; |
| else |
| args->lna = LNA_AUTO; |
| } |
| break; |
| case 'l': |
| args->lnb_name = strdup(optarg); |
| break; |
| case 'S': |
| args->sat_number = strtoul(optarg, NULL, 0); |
| break; |
| case 'U': |
| args->freq_bpf = strtoul(optarg, NULL, 0); |
| break; |
| case 'W': |
| args->diseqc_wait = strtoul(optarg, NULL, 0); |
| break; |
| case 's': |
| args->silent++; |
| break; |
| case 'v': |
| args->verbose++; |
| break; |
| case 'A': |
| args->n_apid = strtoul(optarg, NULL, 0); |
| break; |
| case 'V': |
| args->n_vpid = strtoul(optarg, NULL, 0); |
| break; |
| case 'P': |
| args->all_pids++; |
| break; |
| case 'm': |
| args->traffic_monitor = 1; |
| break; |
| case 'N': |
| args->non_human = 1; |
| break; |
| case 'X': |
| args->low_traffic = atoi(optarg); |
| break; |
| case 'L': |
| args->search = strdup(optarg); |
| break; |
| case 'C': |
| args->cc = strndup(optarg, 2); |
| break; |
| case 'H': |
| args->server = strdup(optarg); |
| break; |
| case 'T': |
| args->port = atoi(optarg); |
| break; |
| case 'D': |
| args->dvr_pipe = strdup(optarg); |
| break; |
| case '?': |
| argp_state_help(state, state->out_stream, |
| ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG |
| | ARGP_HELP_DOC); |
| fprintf(state->out_stream, _("\nReport bugs to %s.\n"), argp_program_bug_address); |
| exit(0); |
| case -4: |
| fprintf (state->out_stream, "%s\n", argp_program_version); |
| exit(0); |
| case -3: |
| argp_state_help(state, state->out_stream, ARGP_HELP_USAGE); |
| exit(0); |
| default: |
| return ARGP_ERR_UNKNOWN; |
| }; |
| return 0; |
| } |
| |
| static char *print_bytes(float val) |
| { |
| static char buf[20]; |
| char *prefix = ""; |
| |
| if (val >= 500 * 1024 * 1024) { |
| prefix = "G"; |
| val /= 1024 * 1024 * 1024.; |
| } else if (val >= 500 * 1024) { |
| prefix = "M"; |
| val /= 1024 * 1024.; |
| } else if (val >= 500) { |
| prefix = "K"; |
| val /= 1024.; |
| } |
| if (*prefix) { |
| if (snprintf(buf, sizeof(buf), "%8.3f %s", val, prefix) <= 0) |
| return " NaN "; |
| } else { |
| if (snprintf(buf, sizeof(buf), "%9.3f ", val) <= 0) |
| return " NaN "; |
| } |
| |
| return buf; |
| } |
| |
| int do_traffic_monitor(struct arguments *args, struct dvb_device *dvb, |
| int out_fd, int timeout) |
| { |
| struct dvb_open_descriptor *fd, *dvr_fd; |
| struct timespec startt; |
| struct dvb_v5_fe_parms *parms = dvb->fe_parms; |
| unsigned long long pidt[0x2001], wait, cont_err = 0; |
| unsigned long long err_cnt[0x2000]; |
| signed char pid_cont[0x2000]; |
| int i, first = 1; |
| |
| memset(pidt, 0, sizeof(pidt)); |
| memset(err_cnt, 0, sizeof(err_cnt)); |
| memset(pid_cont, 0, sizeof(pid_cont)); |
| |
| args->exit_after_tuning = 1; |
| check_frontend(args, parms); |
| |
| dvr_fd = dvb_dev_open(dvb, args->dvr_dev, O_RDONLY); |
| if (!dvr_fd) |
| return -1; |
| |
| fprintf(stderr, _("dvb_dev_set_bufsize: buffer set to %d\n"), DVB_BUF_SIZE); |
| dvb_dev_set_bufsize(dvr_fd, DVB_BUF_SIZE); |
| |
| fd = dvb_dev_open(dvb, args->demux_dev, O_RDWR); |
| if (!fd) { |
| dvb_dev_close(dvr_fd); |
| return -1; |
| } |
| |
| if (args->silent < 2) |
| fprintf(stderr, _(" dvb_set_pesfilter to 0x2000\n")); |
| if (dvb_dev_dmx_set_pesfilter(fd, 0x2000, DMX_PES_OTHER, |
| DMX_OUT_TS_TAP, 0) < 0) { |
| dvb_dev_close(dvr_fd); |
| dvb_dev_close(fd); |
| return -1; |
| } |
| |
| if (clock_gettime(CLOCK_MONOTONIC, &startt)) { |
| fprintf(stderr, _("Can't get timespec\n")); |
| return -1; |
| } |
| |
| wait = 1000; |
| |
| monitor_log(_("%.2fs: Starting capture\n")); |
| while (1) { |
| struct timespec *elapsed; |
| unsigned char buffer[BUFLEN]; |
| int pid, ok, diff; |
| ssize_t r; |
| |
| if (timeout_flag) |
| break; |
| |
| if ((r = dvb_dev_read(dvr_fd, buffer, BUFLEN)) <= 0) { |
| if (r == -EOVERFLOW) { |
| monitor_log(_("%.2fs: buffer overrun\n")); |
| continue; |
| } |
| monitor_log(_("%.2fs: read() returned error %zd\n"), r); |
| break; |
| } |
| |
| /* |
| * It takes a while for a DVB device to start streaming, as the |
| * hardware may be waiting for some locks. The safest way to |
| * ensure that a program record will have the start amount of |
| * time specified by the user is to restart the timeout alarm |
| * here, after the first succeded read. |
| */ |
| if (first) { |
| if (timeout > 0) |
| alarm(timeout); |
| first = 0; |
| } |
| if (out_fd >= 0) { |
| if (write(out_fd, buffer, r) < 0) { |
| PERROR(_("Write failed")); |
| break; |
| } |
| } |
| if (r != BUFLEN) { |
| monitor_log(_("%.2fs: only read %zd bytes\n"), r); |
| break; |
| } |
| |
| for (i = 0; i < BUFLEN; i += 188) { |
| struct dvb_ts_packet_header *h = (void *)&buffer[i]; |
| if (h->sync_byte != 0x47) { |
| monitor_log(_("%.2fs: invalid sync byte. Discarding %zd bytes\n"), r); |
| continue; |
| } |
| |
| bswap16(h->bitfield); |
| |
| #if 0 |
| /* |
| * ITU-T Rec. H.222.0 decoders shall discard Transport |
| * Stream packets with the adaptation_field_control |
| * field set to a value of '00' (invalid). Packets with |
| * a value of '01' are NULL packets. Yet, as those are |
| * actually part of the stream, we won't be discarding, |
| * as we want to take them into account for traffic |
| * estimation purposes. |
| */ |
| if (h->adaptation_field_control == 0) |
| continue; |
| #endif |
| ok = 1; |
| pid = h->pid; |
| |
| if (pid > 0x1fff) { |
| monitor_log(_("%.2fs: invalid pid: 0x%04x\n"), |
| pid); |
| pid = 0x1fff; |
| } |
| |
| /* |
| * After 1 second of processing, check if are there |
| * any issues with regards to frame continuity for |
| * non-NULL packets. |
| * |
| * According to ITU-T H.222.0 | ISO/IEC 13818-1, the |
| * continuity counter isn't incremented if the packet |
| * is 00 or 10. It is only incremented on odd values. |
| * |
| * Also, don't check continuity errors on the first |
| * second, as the frontend is still starting streaming |
| */ |
| if (pid < 0x1fff && h->adaptation_field_control & 1) { |
| int discontinued = 0; |
| |
| if (h->adaptation_field_control & 2) { |
| if (h->adaptation_field_length >= 1) { |
| discontinued = h->discontinued; |
| } else { |
| monitor_log(_("%.2fs: pid %d has adaption layer, but size is too small!\n"), |
| pid); |
| } |
| } |
| |
| if (wait < 2000) |
| discontinued = 1; |
| |
| if (!discontinued && pid_cont[pid] >= 0) { |
| unsigned int next = (pid_cont[pid] + 1) % 16; |
| if (next != h->continuity_counter) { |
| monitor_log(_("%.2fs: pid %d, expecting %d received %d\n"), |
| pid, next, |
| h->continuity_counter); |
| discontinued = 1; |
| cont_err++; |
| err_cnt[pid]++; |
| } |
| } |
| if (discontinued) |
| pid_cont[pid] = -1; |
| else |
| pid_cont[pid] = h->continuity_counter; |
| } |
| |
| if (args->search) { |
| int i, sl = strlen(args->search); |
| ok = 0; |
| if (pid != 0x1fff) { |
| for (i = 0; i < (188 - sl); ++i) { |
| if (!memcmp((char *)h + i, args->search, sl)) |
| ok = 1; |
| } |
| } |
| } |
| |
| if (ok) { |
| pidt[pid]++; |
| pidt[0x2000]++; |
| } |
| } |
| |
| elapsed = elapsed_time(&startt); |
| if (!elapsed) |
| diff = wait; |
| else |
| diff = (unsigned long long)elapsed->tv_sec * 1000 |
| + elapsed->tv_nsec * 1000 / NANO_SECONDS_IN_SEC; |
| |
| if (diff > wait) { |
| unsigned long long other_pidt = 0, other_err_cnt = 0; |
| |
| if (isatty(STDOUT_FILENO)) |
| printf("\x1b[1H\x1b[2J"); |
| |
| args->n_status_lines = 0; |
| printf(_(" PID FREQ SPEED TOTAL\n")); |
| int _pid = 0; |
| for (_pid = 0; _pid < 0x2000; _pid++) { |
| if (pidt[_pid]) { |
| if (args->low_traffic && (pidt[_pid] * 1000. / diff) < args->low_traffic) { |
| other_pidt += pidt[_pid]; |
| other_err_cnt += err_cnt[_pid]; |
| continue; |
| } |
| printf("%5d %9.2f p/s %sbps ", |
| _pid, |
| pidt[_pid] * 1000. / diff, |
| print_bytes(pidt[_pid] * 1000. * 8 * 188/ diff)); |
| if (pidt[_pid] * 188 / 1024) |
| printf("%8llu KB", (pidt[_pid] * 188 + 512) / 1024); |
| else |
| printf(" %8llu B", pidt[_pid] * 188); |
| if (err_cnt[_pid] > 0) |
| printf(" %8llu continuity errors", |
| err_cnt[_pid]); |
| |
| printf("\n"); |
| } |
| } |
| if (other_pidt) { |
| printf(_("OTHER")); |
| printf(" %9.2f p/s %sbps ", |
| other_pidt * 1000. / diff, |
| print_bytes(other_pidt * 1000. * 8 * 188/ diff)); |
| if (other_pidt * 188 / 1024) |
| printf("%8llu KB", (other_pidt * 188 + 512) / 1024); |
| else |
| printf(" %8llu B", other_pidt * 188); |
| if (other_err_cnt > 0) |
| printf(" %8llu continuity errors", |
| other_err_cnt); |
| printf("\n"); |
| } |
| |
| /* 0x2000 is the total traffic */ |
| printf("TOT %11.2f p/s %sbps %8llu KB\n", |
| pidt[_pid] * 1000. / diff, |
| print_bytes(pidt[_pid] * 1000. * 8 * 188/ diff), |
| (pidt[_pid] * 188 + 512) / 1024); |
| printf("\n"); |
| get_show_stats(stdout, args, parms, 0); |
| wait += 1000; |
| if (cont_err) |
| printf("CONTINUITY errors: %llu\n", cont_err); |
| } |
| } |
| monitor_log(_("%.2fs: Stopping capture\n")); |
| dvb_dev_close(dvr_fd); |
| dvb_dev_close(fd); |
| return 0; |
| } |
| |
| static void set_signals(struct arguments *args) |
| { |
| signal(SIGTERM, do_timeout); |
| signal(SIGINT, do_timeout); |
| if (args->timeout > 0) { |
| signal(SIGALRM, do_timeout); |
| alarm(args->timeout); |
| } |
| } |
| |
| static char *default_dvr_pipe = "/tmp/dvr-pipe"; |
| |
| int main(int argc, char **argv) |
| { |
| struct arguments args = {}; |
| char *homedir = getenv("HOME"); |
| char *channel = NULL; |
| int lnb = -1, idx = -1; |
| int vpid = -1, apid = -1, sid = -1; |
| int pmtpid = 0; |
| struct dvb_open_descriptor *pat_fd = NULL, *pmt_fd = NULL; |
| struct dvb_open_descriptor *sdt_fd = NULL; |
| struct dvb_open_descriptor *sid_fd = NULL, *dvr_fd = NULL; |
| struct dvb_open_descriptor *audio_fd = NULL, *video_fd = NULL; |
| int file_fd = -1; |
| int err = -1; |
| int r, ret; |
| struct dvb_v5_fe_parms *parms = NULL; |
| struct dvb_device *dvb; |
| struct dvb_dev_list *dvb_dev; |
| const struct argp argp = { |
| .options = options, |
| .parser = parse_opt, |
| .doc = N_("DVB zap utility"), |
| .args_doc = N_("<channel name> [or <frequency> if in monitor mode]"), |
| }; |
| |
| #ifdef ENABLE_NLS |
| setlocale (LC_ALL, ""); |
| bindtextdomain (PACKAGE, LOCALEDIR); |
| textdomain (PACKAGE); |
| #endif |
| |
| args.sat_number = -1; |
| args.lna = LNA_AUTO; |
| args.input_format = FILE_DVBV5; |
| args.dvr_pipe = default_dvr_pipe; |
| args.low_traffic = 1; |
| |
| if (argp_parse(&argp, argc, argv, ARGP_NO_HELP | ARGP_NO_EXIT, &idx, &args)) { |
| argp_help(&argp, stderr, ARGP_HELP_SHORT_USAGE, PROGRAM_NAME); |
| return -1; |
| } |
| |
| if (idx < argc) |
| channel = argv[idx]; |
| |
| if (!channel) { |
| argp_help(&argp, stderr, ARGP_HELP_STD_HELP, PROGRAM_NAME); |
| return -1; |
| } |
| |
| if (args.input_format == FILE_UNKNOWN) { |
| ERROR("Please specify a valid format\n"); |
| argp_help(&argp, stderr, ARGP_HELP_STD_HELP, PROGRAM_NAME); |
| return -1; |
| } |
| |
| if (!args.traffic_monitor && args.search) { |
| ERROR("search string can be used only on monitor mode\n"); |
| argp_help(&argp, stderr, ARGP_HELP_STD_HELP, PROGRAM_NAME); |
| return -1; |
| } |
| |
| if (args.lnb_name) { |
| lnb = dvb_sat_search_lnb(args.lnb_name); |
| if (lnb < 0) { |
| printf(_("Please select one of the LNBf's below:\n")); |
| dvb_print_all_lnb(); |
| exit(1); |
| } else { |
| printf(_("Using LNBf ")); |
| dvb_print_lnb(lnb); |
| } |
| } |
| |
| dvb = dvb_dev_alloc(); |
| if (!dvb) |
| return -1; |
| |
| if (args.server && args.port) { |
| printf(_("Connecting to %s:%d\n"), args.server, args.port); |
| ret = dvb_dev_remote_init(dvb, args.server, args.port); |
| if (ret < 0) |
| return -1; |
| } |
| |
| dvb_dev_set_log(dvb, args.verbose, NULL); |
| dvb_dev_find(dvb, NULL, NULL); |
| parms = dvb->fe_parms; |
| |
| dvb_dev = dvb_dev_seek_by_adapter(dvb, args.adapter, args.demux, DVB_DEVICE_DEMUX); |
| if (!dvb_dev) { |
| fprintf(stderr, _("Couldn't find demux device node\n")); |
| dvb_dev_free(dvb); |
| return -1; |
| } |
| args.demux_dev = dvb_dev->sysname; |
| |
| dvb_dev = dvb_dev_seek_by_adapter(dvb, args.adapter, args.demux, DVB_DEVICE_DVR); |
| if (!dvb_dev) { |
| fprintf(stderr, _("Couldn't find dvr device node\n")); |
| dvb_dev_free(dvb); |
| return -1; |
| } |
| args.dvr_dev = dvb_dev->sysname; |
| args.dvr_fname = dvb_dev->path; |
| |
| if (args.silent < 2) |
| fprintf(stderr, _("using demux '%s'\n"), args.demux_dev); |
| |
| if (!args.confname) { |
| if (!homedir) |
| ERROR("$HOME not set"); |
| r = asprintf(&args.confname, "%s/.tzap/%i/%s", |
| homedir, args.adapter, CHANNEL_FILE); |
| if (access(args.confname, R_OK)) { |
| free(args.confname); |
| r = asprintf(&args.confname, "%s/.tzap/%s", |
| homedir, CHANNEL_FILE); |
| } |
| } |
| fprintf(stderr, _("reading channels from file '%s'\n"), args.confname); |
| |
| dvb_dev = dvb_dev_seek_by_adapter(dvb, args.adapter, args.frontend, |
| DVB_DEVICE_FRONTEND); |
| if (!dvb_dev) |
| return -1; |
| |
| if (!dvb_dev_open(dvb, dvb_dev->sysname, O_RDWR)) |
| goto err; |
| if (lnb >= 0) |
| parms->lnb = dvb_sat_get_lnb(lnb); |
| if (args.sat_number >= 0) |
| parms->sat_number = args.sat_number; |
| parms->diseqc_wait = args.diseqc_wait; |
| parms->freq_bpf = args.freq_bpf; |
| parms->lna = args.lna; |
| |
| r = dvb_fe_set_default_country(parms, args.cc); |
| if (r < 0) |
| fprintf(stderr, _("Failed to set the country code:%s\n"), args.cc); |
| |
| if (parse(&args, parms, channel, &vpid, &apid, &sid)) |
| goto err; |
| |
| if (setup_frontend(&args, parms) < 0) |
| goto err; |
| |
| if (args.exit_after_tuning) { |
| set_signals(&args); |
| err = 0; |
| check_frontend(&args, parms); |
| goto err; |
| } |
| |
| if (args.traffic_monitor) { |
| if (args.filename) { |
| file_fd = open(args.filename, |
| O_LARGEFILE | |
| O_WRONLY | O_CREAT | O_TRUNC, |
| 0644); |
| if (file_fd < 0) { |
| PERROR(_("open of '%s' failed"), args.filename); |
| return -1; |
| } |
| } |
| set_signals(&args); |
| err = do_traffic_monitor(&args, dvb, file_fd, args.timeout); |
| goto err; |
| } |
| |
| if (args.rec_psi) { |
| if (sid < 0) { |
| fprintf(stderr, _("Service id 0x%04x was not specified at the file\n"), |
| sid); |
| goto err; |
| } |
| |
| sid_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR); |
| if (!sid_fd) { |
| ERROR("opening sid demux failed"); |
| return -1; |
| } |
| pmtpid = dvb_dev_dmx_get_pmt_pid(sid_fd, sid); |
| dvb_dev_close(sid_fd); |
| if (pmtpid <= 0) { |
| fprintf(stderr, _("couldn't find pmt-pid for sid %04x\n"), |
| sid); |
| |
| goto err; |
| } |
| |
| pat_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR); |
| if (!pat_fd) { |
| ERROR("opening pat demux failed"); |
| goto err; |
| } |
| if (dvb_dev_dmx_set_pesfilter(pat_fd, 0, DMX_PES_OTHER, |
| args.dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER, |
| args.dvr ? 64 * 1024 : 0) < 0) |
| goto err; |
| |
| pmt_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR); |
| if (!pmt_fd) { |
| ERROR("opening pmt demux failed"); |
| goto err; |
| } |
| if (dvb_dev_dmx_set_pesfilter(pmt_fd, pmtpid, DMX_PES_OTHER, |
| args.dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER, |
| args.dvr ? 64 * 1024 : 0) < 0) |
| goto err; |
| |
| /* |
| * SDT may also be needed in order to play some streams |
| */ |
| sdt_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR); |
| if (!sdt_fd) { |
| ERROR("opening sdt demux failed"); |
| goto err; |
| } |
| if (dvb_dev_dmx_set_pesfilter(sdt_fd, 0x0011, DMX_PES_OTHER, |
| args.dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER, |
| args.dvr ? 64 * 1024 : 0) < 0) |
| goto err; |
| } |
| |
| if (args.all_pids++) { |
| vpid = 0x2000; |
| apid = 0; |
| } |
| if (vpid >= 0) { |
| if (args.silent < 2) { |
| if (vpid == 0x2000) |
| fprintf(stderr, _("pass all PID's to TS\n")); |
| else |
| fprintf(stderr, _("video pid %d\n"), vpid); |
| } |
| video_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR); |
| if (!video_fd) { |
| ERROR("failed opening '%s'", args.demux_dev); |
| goto err; |
| } |
| |
| if (args.silent < 2) |
| fprintf(stderr, _(" dvb_set_pesfilter %d\n"), vpid); |
| |
| fprintf(stderr, _("dvb_dev_set_bufsize: buffer set to %d\n"), DVB_BUF_SIZE); |
| dvb_dev_set_bufsize(video_fd, DVB_BUF_SIZE); |
| |
| if (vpid == 0x2000) { |
| if (dvb_dev_dmx_set_pesfilter(video_fd, vpid, DMX_PES_OTHER, |
| DMX_OUT_TS_TAP, 0) < 0) |
| goto err; |
| } else { |
| if (dvb_dev_dmx_set_pesfilter(video_fd, vpid, DMX_PES_VIDEO, |
| args.dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER, |
| args.dvr ? 64 * 1024 : 0) < 0) |
| goto err; |
| } |
| } |
| |
| if (apid > 0) { |
| if (args.silent < 2) |
| fprintf(stderr, _("audio pid %d\n"), apid); |
| audio_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR); |
| if (!audio_fd) { |
| ERROR("failed opening '%s'", args.demux_dev); |
| goto err; |
| } |
| if (args.silent < 2) |
| fprintf(stderr, _(" dvb_set_pesfilter %d\n"), apid); |
| if (dvb_dev_dmx_set_pesfilter(audio_fd, apid, DMX_PES_AUDIO, |
| args.dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER, |
| args.dvr ? 64 * 1024 : 0) < 0) |
| goto err; |
| } |
| |
| if (((vpid >= 0 && vpid != 0x2000) || apid) && !args.rec_psi) { |
| printf(_("PMT record is disabled.\n" |
| "Please notice that some streams can only be decoded with PMT data.\n" |
| "Use '-p' option to also record PMT.\n")); |
| } |
| |
| set_signals(&args); |
| |
| if (!check_frontend(&args, parms)) { |
| err = 1; |
| fprintf(stderr, _("frontend doesn't lock\n")); |
| goto err; |
| } |
| |
| if (args.dvr) { |
| if (args.filename) { |
| file_fd = STDOUT_FILENO; |
| |
| if (strcmp(args.filename, "-") != 0) { |
| file_fd = open(args.filename, |
| O_LARGEFILE | |
| O_WRONLY | O_CREAT | O_TRUNC, |
| 0644); |
| if (file_fd < 0) { |
| PERROR(_("open of '%s' failed"), |
| args.filename); |
| return -1; |
| } |
| } |
| } |
| |
| if (args.silent < 2) |
| get_show_stats(stderr, &args, parms, 0); |
| |
| if (file_fd >= 0) { |
| dvr_fd = dvb_dev_open(dvb, args.dvr_dev, O_RDONLY); |
| if (!dvr_fd) { |
| ERROR("failed opening '%s'", args.dvr_dev); |
| goto err; |
| } |
| if (!timeout_flag) |
| fprintf(stderr, _("Record to file '%s' started\n"), args.filename); |
| copy_to_file(dvr_fd, file_fd, args.timeout, args.silent); |
| } else if (args.server && args.port) { |
| struct stat st; |
| if (stat(args.dvr_pipe, &st) == -1) { |
| if (mknod(args.dvr_pipe, |
| S_IRUSR | S_IWUSR | S_IFIFO, 0) < 0) { |
| PERROR("Can't create pipe %s", |
| args.dvr_pipe); |
| return -1; |
| } |
| } else { |
| if (!S_ISFIFO(st.st_mode)) { |
| ERROR("%s exists but is not a pipe", |
| args.dvr_pipe); |
| return -1; |
| } |
| } |
| |
| fprintf(stderr, _("DVR pipe interface '%s' will be opened\n"), args.dvr_pipe); |
| |
| dvr_fd = dvb_dev_open(dvb, args.dvr_dev, O_RDONLY); |
| if (!dvr_fd) { |
| ERROR("failed opening '%s'", args.dvr_dev); |
| err = -1; |
| goto err; |
| } |
| |
| file_fd = open(args.dvr_pipe, |
| #ifdef O_LARGEFILE |
| O_LARGEFILE | |
| #endif |
| O_WRONLY, |
| 0644); |
| if (file_fd < 0) { |
| PERROR(_("open of '%s' failed"), |
| args.filename); |
| err = -1; |
| goto err; |
| } |
| copy_to_file(dvr_fd, file_fd, args.timeout, args.silent); |
| } else { |
| if (!timeout_flag) |
| fprintf(stderr, _("DVR interface '%s' can now be opened\n"), args.dvr_fname); |
| |
| get_show_stats(stderr, &args, parms, 1); |
| } |
| if (args.silent < 2) |
| get_show_stats(stderr, &args, parms, 0); |
| } else { |
| /* Wait until timeout or being killed */ |
| while (!timeout_flag) { |
| get_show_stats(stderr, &args, parms, 1); |
| usleep(1000000); |
| } |
| } |
| err = 0; |
| |
| err: |
| dvb_dev_free(dvb); |
| |
| /* |
| * Just to make Valgrind happier. It should be noticed |
| * That, if an error happens or if the program exits via |
| * timeout code at forced mode, it may not free those. |
| */ |
| if (args.confname) |
| free(args.confname); |
| if (args.filename) |
| free(args.filename); |
| if (args.lnb_name) |
| free(args.lnb_name); |
| if (args.search) |
| free(args.search); |
| if (args.server) |
| free(args.server); |
| if (args.dvr_pipe != default_dvr_pipe) |
| free(args.dvr_pipe); |
| |
| return err; |
| } |