aboutsummaryrefslogtreecommitdiffstats
path: root/util/gnutv
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--util/gnutv/Makefile21
-rw-r--r--util/gnutv/gnutv.c365
-rw-r--r--util/gnutv/gnutv.h37
-rw-r--r--util/gnutv/gnutv_ca.c404
-rw-r--r--util/gnutv/gnutv_ca.h40
-rw-r--r--util/gnutv/gnutv_data.c459
-rw-r--r--util/gnutv/gnutv_data.h39
-rw-r--r--util/gnutv/gnutv_dvb.c376
-rw-r--r--util/gnutv/gnutv_dvb.h44
9 files changed, 1785 insertions, 0 deletions
diff --git a/util/gnutv/Makefile b/util/gnutv/Makefile
new file mode 100644
index 0000000..3a679c4
--- /dev/null
+++ b/util/gnutv/Makefile
@@ -0,0 +1,21 @@
+# Makefile for linuxtv.org dvb-apps/util/gnutv
+
+objects = gnutv_ca.o \
+ gnutv_dvb.o \
+ gnutv_data.o
+
+binaries = gnutv
+
+inst_bin = $(binaries)
+
+CPPFLAGS += -I../../lib
+LDFLAGS += -L../../lib/libdvbapi -L../../lib/libdvbcfg -L../../lib/libdvbsec -L../../lib/libdvben50221 -L../../lib/libucsi
+LDLIBS += -ldvbcfg -ldvben50221 -lucsi -ldvbsec -ldvbapi -lpthread
+
+.PHONY: all
+
+all: $(binaries)
+
+$(binaries): $(objects)
+
+include ../../Make.rules
diff --git a/util/gnutv/gnutv.c b/util/gnutv/gnutv.c
new file mode 100644
index 0000000..62f19be
--- /dev/null
+++ b/util/gnutv/gnutv.c
@@ -0,0 +1,365 @@
+/*
+ gnutv utility
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/poll.h>
+#include <libdvbapi/dvbdemux.h>
+#include <libdvbapi/dvbaudio.h>
+#include <libdvbsec/dvbsec_cfg.h>
+#include <libucsi/mpeg/section.h>
+#include "gnutv.h"
+#include "gnutv_dvb.h"
+#include "gnutv_ca.h"
+#include "gnutv_data.h"
+
+
+static void signal_handler(int _signal);
+
+static int quit_app = 0;
+
+void usage(void)
+{
+ static const char *_usage = "\n"
+ " gnutv: A digital tv utility\n"
+ " Copyright (C) 2004, 2005, 2006 Manu Abraham (manu@kromtek.com)\n"
+ " Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)\n\n"
+ " usage: gnutv <options> as follows:\n"
+ " -h help\n"
+ " -adapter <id> adapter to use (default 0)\n"
+ " -frontend <id> frontend to use (default 0)\n"
+ " -demux <id> demux to use (default 0)\n"
+ " -caslotnum <id> ca slot number to use (default 0)\n"
+ " -channels <filename> channels.conf file.\n"
+ " -secfile <filename> Optional sec.conf file.\n"
+ " -secid <secid> ID of the SEC configuration to use, one of:\n"
+ " * UNIVERSAL (default) - Europe, 10800 to 11800 MHz and 11600 to 12700 Mhz,\n"
+ " Dual LO, loband 9750, hiband 10600 MHz.\n"
+ " * DBS - Expressvu, North America, 12200 to 12700 MHz, Single LO, 11250 MHz.\n"
+ " * STANDARD - 10945 to 11450 Mhz, Single LO, 10000 Mhz.\n"
+ " * ENHANCED - Astra, 10700 to 11700 MHz, Single LO, 9750 MHz.\n"
+ " * C-BAND - Big Dish, 3700 to 4200 MHz, Single LO, 5150 Mhz.\n"
+ " * C-MULTI - Big Dish - Multipoint LNBf, 3700 to 4200 MHz,\n"
+ " Dual LO, H:5150MHz, V:5750MHz.\n"
+ " * One of the sec definitions from the secfile if supplied\n"
+ " -out decoder Output to hardware decoder (default)\n"
+ " decoderabypass Output to hardware decoder using audio bypass\n"
+ " dvr Output stream to dvr device\n"
+ " null Do not output anything\n"
+ " stdout Output to stdout\n"
+ " file <filename> Output stream to file\n"
+ " udp <address> <port> Output stream to address:port using udp\n"
+ " udpif <address> <port> <interface> Output stream to address:port using udp\n"
+ " forcing the specified interface\n"
+ " rtp <address> <port> Output stream to address:port using udp-rtp\n"
+ " rtpif <address> <port> <interface> Output stream to address:port using udp-rtp\n"
+ " forcing the specified interface\n"
+ " -timeout <secs> Number of seconds to output channel for\n"
+ " (0=>exit immediately after successful tuning, default is to output forever)\n"
+ " -cammenu Show the CAM menu\n"
+ " -nomoveca Do not attempt to move CA descriptors from stream to programme level\n"
+ " <channel name>\n";
+ fprintf(stderr, "%s\n", _usage);
+
+ exit(1);
+}
+
+int find_channel(struct dvbcfg_zapchannel *channel, void *private_data)
+{
+ struct dvbcfg_zapchannel *tmpchannel = private_data;
+
+ if (strcmp(channel->name, tmpchannel->name) == 0) {
+ memcpy(tmpchannel, channel, sizeof(struct dvbcfg_zapchannel));
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int adapter_id = 0;
+ int frontend_id = 0;
+ int demux_id = 0;
+ int caslot_num = 0;
+ char *chanfile = "/etc/channels.conf";
+ char *secfile = NULL;
+ char *secid = NULL;
+ char *channel_name = NULL;
+ int output_type = OUTPUT_TYPE_DECODER;
+ char *outfile = NULL;
+ char *outhost = NULL;
+ char *outport = NULL;
+ char *outif = NULL;
+ struct addrinfo *outaddrs = NULL;
+ int timeout = -1;
+ int moveca = 1;
+ int cammenu = 0;
+ int argpos = 1;
+ struct gnutv_dvb_params gnutv_dvb_params;
+ struct gnutv_ca_params gnutv_ca_params;
+ int ffaudiofd = -1;
+ int usertp = 0;
+
+ while(argpos != argc) {
+ if (!strcmp(argv[argpos], "-h")) {
+ usage();
+ } else if (!strcmp(argv[argpos], "-adapter")) {
+ if ((argc - argpos) < 2)
+ usage();
+ if (sscanf(argv[argpos+1], "%i", &adapter_id) != 1)
+ usage();
+ argpos+=2;
+ } else if (!strcmp(argv[argpos], "-frontend")) {
+ if ((argc - argpos) < 2)
+ usage();
+ if (sscanf(argv[argpos+1], "%i", &frontend_id) != 1)
+ usage();
+ argpos+=2;
+ } else if (!strcmp(argv[argpos], "-demux")) {
+ if ((argc - argpos) < 2)
+ usage();
+ if (sscanf(argv[argpos+1], "%i", &demux_id) != 1)
+ usage();
+ argpos+=2;
+ } else if (!strcmp(argv[argpos], "-caslotnum")) {
+ if ((argc - argpos) < 2)
+ usage();
+ if (sscanf(argv[argpos+1], "%i", &caslot_num) != 1)
+ usage();
+ argpos+=2;
+ } else if (!strcmp(argv[argpos], "-channels")) {
+ if ((argc - argpos) < 2)
+ usage();
+ chanfile = argv[argpos+1];
+ argpos+=2;
+ } else if (!strcmp(argv[argpos], "-secfile")) {
+ if ((argc - argpos) < 2)
+ usage();
+ secfile = argv[argpos+1];
+ argpos+=2;
+ } else if (!strcmp(argv[argpos], "-secid")) {
+ if ((argc - argpos) < 2)
+ usage();
+ secid = argv[argpos+1];
+ argpos+=2;
+ } else if (!strcmp(argv[argpos], "-out")) {
+ if ((argc - argpos) < 2)
+ usage();
+ if (!strcmp(argv[argpos+1], "decoder")) {
+ output_type = OUTPUT_TYPE_DECODER;
+ } else if (!strcmp(argv[argpos+1], "decoderabypass")) {
+ output_type = OUTPUT_TYPE_DECODER_ABYPASS;
+ } else if (!strcmp(argv[argpos+1], "dvr")) {
+ output_type = OUTPUT_TYPE_DVR;
+ } else if (!strcmp(argv[argpos+1], "null")) {
+ output_type = OUTPUT_TYPE_NULL;
+ } else if (!strcmp(argv[argpos+1], "stdout")) {
+ output_type = OUTPUT_TYPE_STDOUT;
+ } else if (!strcmp(argv[argpos+1], "file")) {
+ output_type = OUTPUT_TYPE_FILE;
+ if ((argc - argpos) < 3)
+ usage();
+ outfile = argv[argpos+2];
+ argpos++;
+ } else if ((!strcmp(argv[argpos+1], "udp")) ||
+ (!strcmp(argv[argpos+1], "rtp"))) {
+ output_type = OUTPUT_TYPE_UDP;
+ if ((argc - argpos) < 4)
+ usage();
+
+ if (!strcmp(argv[argpos+1], "rtp"))
+ usertp = 1;
+ outhost = argv[argpos+2];
+ outport = argv[argpos+3];
+ argpos+=2;
+ } else if ((!strcmp(argv[argpos+1], "udpif")) ||
+ (!strcmp(argv[argpos+1], "rtpif"))) {
+ output_type = OUTPUT_TYPE_UDP;
+ if ((argc - argpos) < 5)
+ usage();
+
+ if (!strcmp(argv[argpos+1], "rtpif"))
+ usertp = 1;
+ outhost = argv[argpos+2];
+ outport = argv[argpos+3];
+ outif = argv[argpos+4];
+ argpos+=3;
+ } else {
+ usage();
+ }
+ argpos+=2;
+ } else if (!strcmp(argv[argpos], "-timeout")) {
+ if ((argc - argpos) < 2)
+ usage();
+ if (sscanf(argv[argpos+1], "%i", &timeout) != 1)
+ usage();
+ argpos+=2;
+ } else if (!strcmp(argv[argpos], "-nomoveca")) {
+ moveca = 0;
+ argpos++;
+ } else if (!strcmp(argv[argpos], "-cammenu")) {
+ cammenu = 1;
+ argpos++;
+ } else {
+ if ((argc - argpos) != 1)
+ usage();
+ channel_name = argv[argpos];
+ argpos++;
+ }
+ }
+
+ // the user didn't select anything!
+ if ((channel_name == NULL) && (!cammenu))
+ usage();
+
+ // resolve host/port
+ if ((outhost != NULL) && (outport != NULL)) {
+ int res;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ if ((res = getaddrinfo(outhost, outport, &hints, &outaddrs)) != 0) {
+ fprintf(stderr, "Unable to resolve requested address: %s\n", gai_strerror(res));
+ exit(1);
+ }
+ }
+
+ // setup any signals
+ signal(SIGINT, signal_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ // start the CA stuff
+ gnutv_ca_params.adapter_id = adapter_id;
+ gnutv_ca_params.caslot_num = caslot_num;
+ gnutv_ca_params.cammenu = cammenu;
+ gnutv_ca_params.moveca = moveca;
+ gnutv_ca_start(&gnutv_ca_params);
+
+ // frontend setup if a channel name was supplied
+ if ((!cammenu) && (channel_name != NULL)) {
+ // find the requested channel
+ if (strlen(channel_name) >= sizeof(gnutv_dvb_params.channel.name)) {
+ fprintf(stderr, "Channel name is too long %s\n", channel_name);
+ exit(1);
+ }
+ FILE *channel_file = fopen(chanfile, "r");
+ if (channel_file == NULL) {
+ fprintf(stderr, "Could open channel file %s\n", chanfile);
+ exit(1);
+ }
+ memcpy(gnutv_dvb_params.channel.name, channel_name, strlen(channel_name) + 1);
+ if (dvbcfg_zapchannel_parse(channel_file, find_channel, &gnutv_dvb_params.channel) != 1) {
+ fprintf(stderr, "Unable to find requested channel %s\n", channel_name);
+ exit(1);
+ }
+ fclose(channel_file);
+
+ // default SEC with a DVBS card
+ if ((secid == NULL) && (gnutv_dvb_params.channel.fe_type == DVBFE_TYPE_DVBS))
+ secid = "UNIVERSAL";
+
+ // look it up if one were supplied
+ gnutv_dvb_params.valid_sec = 0;
+ if (secid != NULL) {
+ if (dvbsec_cfg_find(secfile, secid,
+ &gnutv_dvb_params.sec)) {
+ fprintf(stderr, "Unable to find suitable sec/lnb configuration for channel\n");
+ exit(1);
+ }
+ gnutv_dvb_params.valid_sec = 1;
+ }
+
+ // open the frontend
+ gnutv_dvb_params.fe = dvbfe_open(adapter_id, frontend_id, 0);
+ if (gnutv_dvb_params.fe == NULL) {
+ fprintf(stderr, "Failed to open frontend\n");
+ exit(1);
+ }
+
+ // failover decoder to dvr output if decoder not available
+ if ((output_type == OUTPUT_TYPE_DECODER) ||
+ (output_type == OUTPUT_TYPE_DECODER_ABYPASS)) {
+ ffaudiofd = dvbaudio_open(adapter_id, 0);
+ if (ffaudiofd < 0) {
+ fprintf(stderr, "Cannot open decoder; defaulting to dvr output\n");
+ output_type = OUTPUT_TYPE_DVR;
+ }
+ }
+
+ // start the DVB stuff
+ gnutv_dvb_params.adapter_id = adapter_id;
+ gnutv_dvb_params.frontend_id = frontend_id;
+ gnutv_dvb_params.demux_id = demux_id;
+ gnutv_dvb_params.output_type = output_type;
+ gnutv_dvb_start(&gnutv_dvb_params);
+
+ // start the data stuff
+ gnutv_data_start(output_type, ffaudiofd, adapter_id, demux_id, outfile, outif, outaddrs, usertp);
+ }
+
+ // the UI
+ time_t start = 0;
+ while(!quit_app) {
+ if (gnutv_dvb_locked() && (start == 0))
+ start = time(NULL);
+
+ // the timeout
+ if ((timeout != -1) && (start != 0)) {
+ if ((time(NULL) - start) >= timeout)
+ break;
+ }
+
+ if (cammenu)
+ gnutv_ca_ui();
+ else
+ usleep(1);
+ }
+
+ // stop data handling
+ gnutv_data_stop();
+
+ // shutdown DVB stuff
+ if (channel_name != NULL)
+ gnutv_dvb_stop();
+
+ // shutdown CA stuff
+ gnutv_ca_stop();
+
+ // done
+ exit(0);
+}
+
+static void signal_handler(int _signal)
+{
+ (void) _signal;
+
+ if (!quit_app) {
+ quit_app = 1;
+ }
+}
diff --git a/util/gnutv/gnutv.h b/util/gnutv/gnutv.h
new file mode 100644
index 0000000..02ed041
--- /dev/null
+++ b/util/gnutv/gnutv.h
@@ -0,0 +1,37 @@
+/*
+ gnutv utility
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __CA_gnutv_H__
+#define __CA_gnutv_H__
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#define OUTPUT_TYPE_DECODER 0
+#define OUTPUT_TYPE_DECODER_ABYPASS 1
+#define OUTPUT_TYPE_DVR 2
+#define OUTPUT_TYPE_NULL 3
+#define OUTPUT_TYPE_FILE 4
+#define OUTPUT_TYPE_UDP 5
+#define OUTPUT_TYPE_STDOUT 6
+
+#endif
diff --git a/util/gnutv/gnutv_ca.c b/util/gnutv/gnutv_ca.c
new file mode 100644
index 0000000..5830f21
--- /dev/null
+++ b/util/gnutv/gnutv_ca.c
@@ -0,0 +1,404 @@
+/*
+ gnutv utility
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <pthread.h>
+#include <libdvben50221/en50221_stdcam.h>
+#include "gnutv.h"
+#include "gnutv_ca.h"
+
+
+
+#define MMI_STATE_CLOSED 0
+#define MMI_STATE_OPEN 1
+#define MMI_STATE_ENQ 2
+#define MMI_STATE_MENU 3
+
+static int gnutv_ca_info_callback(void *arg, uint8_t slot_id, uint16_t session_number, uint32_t ca_id_count, uint16_t *ca_ids);
+static int gnutv_ai_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t application_type, uint16_t application_manufacturer,
+ uint16_t manufacturer_code, uint8_t menu_string_length,
+ uint8_t *menu_string);
+
+static int gnutv_mmi_close_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t cmd_id, uint8_t delay);
+static int gnutv_mmi_display_control_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t cmd_id, uint8_t mmi_mode);
+static int gnutv_mmi_enq_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t blind_answer, uint8_t expected_answer_length,
+ uint8_t *text, uint32_t text_size);
+static int gnutv_mmi_menu_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ struct en50221_app_mmi_text *title,
+ struct en50221_app_mmi_text *sub_title,
+ struct en50221_app_mmi_text *bottom,
+ uint32_t item_count, struct en50221_app_mmi_text *items,
+ uint32_t item_raw_length, uint8_t *items_raw);
+static void *camthread_func(void* arg);
+
+static struct en50221_transport_layer *tl = NULL;
+static struct en50221_session_layer *sl = NULL;
+static struct en50221_stdcam *stdcam = NULL;
+
+static int ca_resource_connected = 0;
+static int mmi_state = MMI_STATE_CLOSED;
+static int mmi_enq_blind;
+static int mmi_enq_length;
+
+static int camthread_shutdown = 0;
+static pthread_t camthread;
+int moveca = 0;
+int seenpmt = 0;
+int cammenu = 0;
+
+char ui_line[256];
+uint32_t ui_linepos = 0;
+
+
+void gnutv_ca_start(struct gnutv_ca_params *params)
+{
+ // create transport layer
+ tl = en50221_tl_create(1, 16);
+ if (tl == NULL) {
+ fprintf(stderr, "Failed to create transport layer\n");
+ return;
+ }
+
+ // create session layer
+ sl = en50221_sl_create(tl, 16);
+ if (sl == NULL) {
+ fprintf(stderr, "Failed to create session layer\n");
+ en50221_tl_destroy(tl);
+ return;
+ }
+
+ // create the stdcam instance
+ stdcam = en50221_stdcam_create(params->adapter_id, params->caslot_num, tl, sl);
+ if (stdcam == NULL) {
+ en50221_sl_destroy(sl);
+ en50221_tl_destroy(tl);
+ return;
+ }
+
+ // hook up the AI callbacks
+ if (stdcam->ai_resource) {
+ en50221_app_ai_register_callback(stdcam->ai_resource, gnutv_ai_callback, stdcam);
+ }
+
+ // hook up the CA callbacks
+ if (stdcam->ca_resource) {
+ en50221_app_ca_register_info_callback(stdcam->ca_resource, gnutv_ca_info_callback, stdcam);
+ }
+
+ // hook up the MMI callbacks
+ if (params->cammenu) {
+ if (stdcam->mmi_resource) {
+ en50221_app_mmi_register_close_callback(stdcam->mmi_resource, gnutv_mmi_close_callback, stdcam);
+ en50221_app_mmi_register_display_control_callback(stdcam->mmi_resource, gnutv_mmi_display_control_callback, stdcam);
+ en50221_app_mmi_register_enq_callback(stdcam->mmi_resource, gnutv_mmi_enq_callback, stdcam);
+ en50221_app_mmi_register_menu_callback(stdcam->mmi_resource, gnutv_mmi_menu_callback, stdcam);
+ en50221_app_mmi_register_list_callback(stdcam->mmi_resource, gnutv_mmi_menu_callback, stdcam);
+ } else {
+ fprintf(stderr, "CAM Menus are not supported by this interface hardware\n");
+ exit(1);
+ }
+ }
+
+ // any other stuff
+ moveca = params->moveca;
+ cammenu = params->cammenu;
+
+ // start the cam thread
+ pthread_create(&camthread, NULL, camthread_func, NULL);
+}
+
+void gnutv_ca_stop(void)
+{
+ if (stdcam == NULL)
+ return;
+
+ // shutdown the cam thread
+ camthread_shutdown = 1;
+ pthread_join(camthread, NULL);
+
+ // destroy the stdcam
+ if (stdcam->destroy)
+ stdcam->destroy(stdcam, 1);
+
+ // destroy session layer
+ en50221_sl_destroy(sl);
+
+ // destroy transport layer
+ en50221_tl_destroy(tl);
+}
+
+void gnutv_ca_ui(void)
+{
+ // make up polling structure for stdin
+ struct pollfd pollfd;
+ pollfd.fd = 0;
+ pollfd.events = POLLIN|POLLPRI|POLLERR;
+
+ if (stdcam == NULL)
+ return;
+
+ // is there a character?
+ if (poll(&pollfd, 1, 10) != 1)
+ return;
+ if (pollfd.revents & POLLERR)
+ return;
+
+ // try to read the character
+ char c;
+ if (read(0, &c, 1) != 1)
+ return;
+ if (c == '\r') {
+ return;
+ } else if (c == '\n') {
+ switch(mmi_state) {
+ case MMI_STATE_CLOSED:
+ case MMI_STATE_OPEN:
+ if ((ui_linepos == 0) && (ca_resource_connected)) {
+ en50221_app_ai_entermenu(stdcam->ai_resource, stdcam->ai_session_number);
+ }
+ break;
+
+ case MMI_STATE_ENQ:
+ if (ui_linepos == 0) {
+ en50221_app_mmi_answ(stdcam->mmi_resource, stdcam->mmi_session_number,
+ MMI_ANSW_ID_CANCEL, NULL, 0);
+ } else {
+ en50221_app_mmi_answ(stdcam->mmi_resource, stdcam->mmi_session_number,
+ MMI_ANSW_ID_ANSWER, (uint8_t*) ui_line, ui_linepos);
+ }
+ mmi_state = MMI_STATE_OPEN;
+ break;
+
+ case MMI_STATE_MENU:
+ ui_line[ui_linepos] = 0;
+ en50221_app_mmi_menu_answ(stdcam->mmi_resource, stdcam->mmi_session_number,
+ atoi(ui_line));
+ mmi_state = MMI_STATE_OPEN;
+ break;
+ }
+ ui_linepos = 0;
+ } else {
+ if (ui_linepos < (sizeof(ui_line)-1)) {
+ ui_line[ui_linepos++] = c;
+ }
+ }
+}
+
+int gnutv_ca_new_pmt(struct mpeg_pmt_section *pmt)
+{
+ uint8_t capmt[4096];
+ int size;
+
+ if (stdcam == NULL)
+ return -1;
+
+ if (ca_resource_connected) {
+ fprintf(stderr, "Received new PMT - sending to CAM...\n");
+
+ // translate it into a CA PMT
+ int listmgmt = CA_LIST_MANAGEMENT_ONLY;
+ if (seenpmt) {
+ listmgmt = CA_LIST_MANAGEMENT_UPDATE;
+ }
+ seenpmt = 1;
+
+ if ((size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), moveca, listmgmt,
+ CA_PMT_CMD_ID_OK_DESCRAMBLING)) < 0) {
+ fprintf(stderr, "Failed to format PMT\n");
+ return -1;
+ }
+
+ // set it
+ if (en50221_app_ca_pmt(stdcam->ca_resource, stdcam->ca_session_number, capmt, size)) {
+ fprintf(stderr, "Failed to send PMT\n");
+ return -1;
+ }
+
+ // we've seen this PMT
+ return 1;
+ }
+
+ return 0;
+}
+
+void gnutv_ca_new_dvbtime(time_t dvb_time)
+{
+ if (stdcam == NULL)
+ return;
+
+ if (stdcam->dvbtime)
+ stdcam->dvbtime(stdcam, dvb_time);
+}
+
+static void *camthread_func(void* arg)
+{
+ (void) arg;
+ int entered_menu = 0;
+
+ while(!camthread_shutdown) {
+ stdcam->poll(stdcam);
+
+ if ((!entered_menu) && cammenu && ca_resource_connected && stdcam->mmi_resource) {
+ en50221_app_ai_entermenu(stdcam->ai_resource, stdcam->ai_session_number);
+ entered_menu = 1;
+ }
+ }
+
+ return 0;
+}
+
+static int gnutv_ai_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t application_type, uint16_t application_manufacturer,
+ uint16_t manufacturer_code, uint8_t menu_string_length,
+ uint8_t *menu_string)
+{
+ (void) arg;
+ (void) slot_id;
+ (void) session_number;
+
+ fprintf(stderr, "CAM Application type: %02x\n", application_type);
+ fprintf(stderr, "CAM Application manufacturer: %04x\n", application_manufacturer);
+ fprintf(stderr, "CAM Manufacturer code: %04x\n", manufacturer_code);
+ fprintf(stderr, "CAM Menu string: %.*s\n", menu_string_length, menu_string);
+
+ return 0;
+}
+
+static int gnutv_ca_info_callback(void *arg, uint8_t slot_id, uint16_t session_number, uint32_t ca_id_count, uint16_t *ca_ids)
+{
+ (void) arg;
+ (void) slot_id;
+ (void) session_number;
+
+ fprintf(stderr, "CAM supports the following ca system ids:\n");
+ uint32_t i;
+ for(i=0; i< ca_id_count; i++) {
+ fprintf(stderr, " 0x%04x\n", ca_ids[i]);
+ }
+ ca_resource_connected = 1;
+ return 0;
+}
+
+static int gnutv_mmi_close_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t cmd_id, uint8_t delay)
+{
+ (void) arg;
+ (void) slot_id;
+ (void) session_number;
+ (void) cmd_id;
+ (void) delay;
+
+ // note: not entirely correct as its supposed to delay if asked
+ mmi_state = MMI_STATE_CLOSED;
+ return 0;
+}
+
+static int gnutv_mmi_display_control_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t cmd_id, uint8_t mmi_mode)
+{
+ struct en50221_app_mmi_display_reply_details reply;
+ (void) arg;
+ (void) slot_id;
+
+ // don't support any commands but set mode
+ if (cmd_id != MMI_DISPLAY_CONTROL_CMD_ID_SET_MMI_MODE) {
+ en50221_app_mmi_display_reply(stdcam->mmi_resource, session_number,
+ MMI_DISPLAY_REPLY_ID_UNKNOWN_CMD_ID, &reply);
+ return 0;
+ }
+
+ // we only support high level mode
+ if (mmi_mode != MMI_MODE_HIGH_LEVEL) {
+ en50221_app_mmi_display_reply(stdcam->mmi_resource, session_number,
+ MMI_DISPLAY_REPLY_ID_UNKNOWN_MMI_MODE, &reply);
+ return 0;
+ }
+
+ // ack the high level open
+ reply.u.mode_ack.mmi_mode = mmi_mode;
+ en50221_app_mmi_display_reply(stdcam->mmi_resource, session_number,
+ MMI_DISPLAY_REPLY_ID_MMI_MODE_ACK, &reply);
+ mmi_state = MMI_STATE_OPEN;
+ return 0;
+}
+
+static int gnutv_mmi_enq_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t blind_answer, uint8_t expected_answer_length,
+ uint8_t *text, uint32_t text_size)
+{
+ (void) arg;
+ (void) slot_id;
+ (void) session_number;
+
+ fprintf(stderr, "%.*s: ", text_size, text);
+ fflush(stdout);
+
+ mmi_enq_blind = blind_answer;
+ mmi_enq_length = expected_answer_length;
+ mmi_state = MMI_STATE_ENQ;
+ return 0;
+}
+
+static int gnutv_mmi_menu_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ struct en50221_app_mmi_text *title,
+ struct en50221_app_mmi_text *sub_title,
+ struct en50221_app_mmi_text *bottom,
+ uint32_t item_count, struct en50221_app_mmi_text *items,
+ uint32_t item_raw_length, uint8_t *items_raw)
+{
+ (void) arg;
+ (void) slot_id;
+ (void) session_number;
+ (void) item_raw_length;
+ (void) items_raw;
+
+ fprintf(stderr, "------------------------------\n");
+
+ if (title->text_length) {
+ fprintf(stderr, "%.*s\n", title->text_length, title->text);
+ }
+ if (sub_title->text_length) {
+ fprintf(stderr, "%.*s\n", sub_title->text_length, sub_title->text);
+ }
+
+ uint32_t i;
+ fprintf(stderr, "0. Quit menu\n");
+ for(i=0; i< item_count; i++) {
+ fprintf(stderr, "%i. %.*s\n", i+1, items[i].text_length, items[i].text);
+ }
+
+ if (bottom->text_length) {
+ fprintf(stderr, "%.*s\n", bottom->text_length, bottom->text);
+ }
+ fprintf(stderr, "Enter option: ");
+ fflush(stdout);
+
+ mmi_state = MMI_STATE_MENU;
+ return 0;
+}
diff --git a/util/gnutv/gnutv_ca.h b/util/gnutv/gnutv_ca.h
new file mode 100644
index 0000000..7b5d5b3
--- /dev/null
+++ b/util/gnutv/gnutv_ca.h
@@ -0,0 +1,40 @@
+/*
+ gnutv utility
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef gnutv_CA_H
+#define gnutv_CA_H 1
+
+struct gnutv_ca_params {
+ int adapter_id;
+ int caslot_num;
+ int cammenu;
+ int moveca;
+};
+
+extern void gnutv_ca_start(struct gnutv_ca_params *params);
+extern void gnutv_ca_ui(void);
+extern void gnutv_ca_stop(void);
+
+extern int gnutv_ca_new_pmt(struct mpeg_pmt_section *pmt);
+extern void gnutv_ca_new_dvbtime(time_t dvb_time);
+
+#endif
diff --git a/util/gnutv/gnutv_data.c b/util/gnutv/gnutv_data.c
new file mode 100644
index 0000000..7ac0f23
--- /dev/null
+++ b/util/gnutv/gnutv_data.c
@@ -0,0 +1,459 @@
+/*
+ gnutv utility
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE_SOURCE 1
+#define _LARGEFILE64_SOURCE 1
+
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pthread.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <libdvbapi/dvbdemux.h>
+#include <libdvbapi/dvbaudio.h>
+#include <libucsi/mpeg/section.h>
+#include "gnutv.h"
+#include "gnutv_dvb.h"
+#include "gnutv_ca.h"
+#include "gnutv_data.h"
+
+static void *fileoutputthread_func(void* arg);
+static void *udpoutputthread_func(void* arg);
+
+static int gnutv_data_create_decoder_filter(int adapter, int demux, uint16_t pid, int pestype);
+static int gnutv_data_create_dvr_filter(int adapter, int demux, uint16_t pid);
+
+static void gnutv_data_decoder_pmt(struct mpeg_pmt_section *pmt);
+static void gnutv_data_dvr_pmt(struct mpeg_pmt_section *pmt);
+
+static void gnutv_data_append_pid_fd(int pid, int fd);
+static void gnutv_data_free_pid_fds(void);
+
+static pthread_t outputthread;
+static int outfd = -1;
+static int dvrfd = -1;
+static int pat_fd_dvrout = -1;
+static int pmt_fd_dvrout = -1;
+static int outputthread_shutdown = 0;
+
+static int usertp = 0;
+static int adapter_id = -1;
+static int demux_id = -1;
+static int output_type = 0;
+static struct addrinfo *outaddrs = NULL;
+
+struct pid_fd {
+ int pid;
+ int fd;
+};
+static struct pid_fd *pid_fds = NULL;
+static int pid_fds_count = 0;
+
+void gnutv_data_start(int _output_type,
+ int ffaudiofd, int _adapter_id, int _demux_id,
+ char *outfile,
+ char* outif, struct addrinfo *_outaddrs, int _usertp)
+{
+ usertp = _usertp;
+ demux_id = _demux_id;
+ adapter_id = _adapter_id;
+ output_type = _output_type;
+
+ // setup output
+ switch(output_type) {
+ case OUTPUT_TYPE_DECODER:
+ case OUTPUT_TYPE_DECODER_ABYPASS:
+ dvbaudio_set_bypass(ffaudiofd, (output_type == OUTPUT_TYPE_DECODER_ABYPASS) ? 1 : 0);
+ close(ffaudiofd);
+ break;
+
+ case OUTPUT_TYPE_STDOUT:
+ case OUTPUT_TYPE_FILE:
+ if (output_type == OUTPUT_TYPE_FILE) {
+ // open output file
+ outfd = open(outfile, O_WRONLY|O_CREAT|O_LARGEFILE|O_TRUNC, 0644);
+ if (outfd < 0) {
+ fprintf(stderr, "Failed to open output file\n");
+ exit(1);
+ }
+ } else {
+ outfd = STDOUT_FILENO;
+ }
+
+ // open dvr device
+ dvrfd = dvbdemux_open_dvr(adapter_id, 0, 1, 0);
+ if (dvrfd < 0) {
+ fprintf(stderr, "Failed to open DVR device\n");
+ exit(1);
+ }
+
+ pthread_create(&outputthread, NULL, fileoutputthread_func, NULL);
+ break;
+
+ case OUTPUT_TYPE_UDP:
+ outaddrs = _outaddrs;
+
+ // open output socket
+ outfd = socket(outaddrs->ai_family, outaddrs->ai_socktype, outaddrs->ai_protocol);
+ if (outfd < 0) {
+ fprintf(stderr, "Failed to open output socket\n");
+ exit(1);
+ }
+
+ // bind to local interface if requested
+ if (outif != NULL) {
+ if (setsockopt(outfd, SOL_SOCKET, SO_BINDTODEVICE, outif, strlen(outif)) < 0) {
+ fprintf(stderr, "Failed to bind to interface %s\n", outif);
+ exit(1);
+ }
+ }
+
+ // open dvr device
+ dvrfd = dvbdemux_open_dvr(adapter_id, 0, 1, 0);
+ if (dvrfd < 0) {
+ fprintf(stderr, "Failed to open DVR device\n");
+ exit(1);
+ }
+
+ pthread_create(&outputthread, NULL, udpoutputthread_func, NULL);
+ break;
+ }
+
+ // output PAT to DVR if requested
+ switch(output_type) {
+ case OUTPUT_TYPE_DVR:
+ case OUTPUT_TYPE_FILE:
+ case OUTPUT_TYPE_STDOUT:
+ case OUTPUT_TYPE_UDP:
+ pat_fd_dvrout = gnutv_data_create_dvr_filter(adapter_id, demux_id, TRANSPORT_PAT_PID);
+ }
+}
+
+void gnutv_data_stop()
+{
+ // shutdown output thread if necessary
+ if (dvrfd != -1) {
+ outputthread_shutdown = 1;
+ pthread_join(outputthread, NULL);
+ }
+ gnutv_data_free_pid_fds();
+ if (pat_fd_dvrout != -1)
+ close(pat_fd_dvrout);
+ if (pmt_fd_dvrout != -1)
+ close(pmt_fd_dvrout);
+ if (outaddrs)
+ freeaddrinfo(outaddrs);
+}
+
+void gnutv_data_new_pat(int pmt_pid)
+{
+ // output PMT to DVR if requested
+ switch(output_type) {
+ case OUTPUT_TYPE_DVR:
+ case OUTPUT_TYPE_FILE:
+ case OUTPUT_TYPE_STDOUT:
+ case OUTPUT_TYPE_UDP:
+ if (pmt_fd_dvrout != -1)
+ close(pmt_fd_dvrout);
+ pmt_fd_dvrout = gnutv_data_create_dvr_filter(adapter_id, demux_id, pmt_pid);
+ }
+}
+
+int gnutv_data_new_pmt(struct mpeg_pmt_section *pmt)
+{
+ // close all old PID FDs
+ gnutv_data_free_pid_fds();
+
+ // deal with the PMT appropriately
+ switch(output_type) {
+ case OUTPUT_TYPE_DECODER:
+ case OUTPUT_TYPE_DECODER_ABYPASS:
+ gnutv_data_decoder_pmt(pmt);
+ break;
+
+ case OUTPUT_TYPE_DVR:
+ case OUTPUT_TYPE_FILE:
+ case OUTPUT_TYPE_STDOUT:
+ case OUTPUT_TYPE_UDP:
+ gnutv_data_dvr_pmt(pmt);
+ break;
+ }
+
+ return 1;
+}
+
+static void *fileoutputthread_func(void* arg)
+{
+ (void)arg;
+ uint8_t buf[4096];
+ struct pollfd pollfd;
+ int written;
+
+ pollfd.fd = dvrfd;
+ pollfd.events = POLLIN|POLLPRI|POLLERR;
+
+ while(!outputthread_shutdown) {
+ if (poll(&pollfd, 1, 1000) != 1)
+ continue;
+ if (pollfd.revents & POLLERR) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "DVR device read failure\n");
+ return 0;
+ }
+
+ int size = read(dvrfd, buf, sizeof(buf));
+ if (size < 0) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "DVR device read failure\n");
+ return 0;
+ }
+
+ written = 0;
+ while(written < size) {
+ int tmp = write(outfd, buf + written, size - written);
+ if (tmp == -1) {
+ if (errno != EINTR) {
+ fprintf(stderr, "Write error: %m\n");
+ break;
+ }
+ } else {
+ written += tmp;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#define TS_PAYLOAD_SIZE (188*7)
+
+static void *udpoutputthread_func(void* arg)
+{
+ (void)arg;
+ uint8_t buf[12 + TS_PAYLOAD_SIZE];
+ struct pollfd pollfd;
+ int bufsize = 0;
+ int bufbase = 0;
+ int readsize;
+ uint16_t rtpseq = 0;
+
+ pollfd.fd = dvrfd;
+ pollfd.events = POLLIN|POLLPRI|POLLERR;
+
+ if (usertp) {
+ srandom(time(NULL));
+ int ssrc = random();
+ rtpseq = random();
+ buf[0x0] = 0x80;
+ buf[0x1] = 0x21;
+ buf[0x4] = 0x00; // }
+ buf[0x5] = 0x00; // } FIXME: should really be a valid stamp
+ buf[0x6] = 0x00; // }
+ buf[0x7] = 0x00; // }
+ buf[0x8] = ssrc >> 24;
+ buf[0x9] = ssrc >> 16;
+ buf[0xa] = ssrc >> 8;
+ buf[0xb] = ssrc;
+ bufbase = 12;
+ }
+
+ while(!outputthread_shutdown) {
+ if (poll(&pollfd, 1, 1000) != 1)
+ continue;
+ if (pollfd.revents & POLLERR) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "DVR device read failure\n");
+ return 0;
+ }
+
+ readsize = TS_PAYLOAD_SIZE - bufsize;
+ readsize = read(dvrfd, buf + bufbase + bufsize, readsize);
+ if (readsize < 0) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "DVR device read failure\n");
+ return 0;
+ }
+ bufsize += readsize;
+
+ if (bufsize == TS_PAYLOAD_SIZE) {
+ if (usertp) {
+ buf[2] = rtpseq >> 8;
+ buf[3] = rtpseq;
+ }
+ if (sendto(outfd, buf, bufbase + bufsize, 0, outaddrs->ai_addr, outaddrs->ai_addrlen) < 0) {
+ if (errno != EINTR) {
+ fprintf(stderr, "Socket send failure: %m\n");
+ return 0;
+ }
+ }
+ rtpseq++;
+ bufsize = 0;
+ }
+ }
+
+ if (bufsize) {
+ if (usertp) {
+ buf[2] = rtpseq >> 8;
+ buf[3] = rtpseq;
+ }
+ if (sendto(outfd, buf, bufbase + bufsize, 0, outaddrs->ai_addr, outaddrs->ai_addrlen) < 0) {
+ if (errno != EINTR)
+ fprintf(stderr, "Socket send failure: %m\n");
+ }
+ }
+
+ return 0;
+}
+
+static int gnutv_data_create_decoder_filter(int adapter, int demux, uint16_t pid, int pestype)
+{
+ int demux_fd = -1;
+
+ // open the demuxer
+ if ((demux_fd = dvbdemux_open_demux(adapter, demux, 0)) < 0) {
+ return -1;
+ }
+
+ // create a section filter
+ if (dvbdemux_set_pes_filter(demux_fd, pid, DVBDEMUX_INPUT_FRONTEND, DVBDEMUX_OUTPUT_DECODER, pestype, 1)) {
+ close(demux_fd);
+ return -1;
+ }
+
+ // done
+ return demux_fd;
+}
+
+static int gnutv_data_create_dvr_filter(int adapter, int demux, uint16_t pid)
+{
+ int demux_fd = -1;
+
+ // open the demuxer
+ if ((demux_fd = dvbdemux_open_demux(adapter, demux, 0)) < 0) {
+ return -1;
+ }
+
+ // create a section filter
+ if (dvbdemux_set_pid_filter(demux_fd, pid, DVBDEMUX_INPUT_FRONTEND, DVBDEMUX_OUTPUT_DVR, 1)) {
+ close(demux_fd);
+ return -1;
+ }
+
+ // done
+ return demux_fd;
+}
+
+static void gnutv_data_decoder_pmt(struct mpeg_pmt_section *pmt)
+{
+ int audio_pid = -1;
+ int video_pid = -1;
+ struct mpeg_pmt_stream *cur_stream;
+ mpeg_pmt_section_streams_for_each(pmt, cur_stream) {
+ switch(cur_stream->stream_type) {
+ case 1:
+ case 2: // video
+ video_pid = cur_stream->pid;
+ break;
+
+ case 3:
+ case 4: // audio
+ audio_pid = cur_stream->pid;
+ break;
+ }
+ }
+
+ if (audio_pid != -1) {
+ int fd = gnutv_data_create_decoder_filter(adapter_id, demux_id, audio_pid, DVBDEMUX_PESTYPE_AUDIO);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to create dvr filter for PID %i\n", audio_pid);
+ } else {
+ gnutv_data_append_pid_fd(audio_pid, fd);
+ }
+ }
+ if (video_pid != -1) {
+ int fd = gnutv_data_create_decoder_filter(adapter_id, demux_id, video_pid, DVBDEMUX_PESTYPE_VIDEO);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to create dvr filter for PID %i\n", video_pid);
+ } else {
+ gnutv_data_append_pid_fd(video_pid, fd);
+ }
+ }
+ int fd = gnutv_data_create_decoder_filter(adapter_id, demux_id, pmt->pcr_pid, DVBDEMUX_PESTYPE_PCR);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to create dvr filter for PID %i\n", pmt->pcr_pid);
+ } else {
+ gnutv_data_append_pid_fd(pmt->pcr_pid, fd);
+ }
+}
+
+static void gnutv_data_dvr_pmt(struct mpeg_pmt_section *pmt)
+{
+ struct mpeg_pmt_stream *cur_stream;
+ mpeg_pmt_section_streams_for_each(pmt, cur_stream) {
+ int fd = gnutv_data_create_dvr_filter(adapter_id, demux_id, cur_stream->pid);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to create dvr filter for PID %i\n", cur_stream->pid);
+ } else {
+ gnutv_data_append_pid_fd(cur_stream->pid, fd);
+ }
+ }
+}
+
+static void gnutv_data_append_pid_fd(int pid, int fd)
+{
+ struct pid_fd *tmp;
+ if ((tmp = realloc(pid_fds, (pid_fds_count +1) * sizeof(struct pid_fd))) == NULL) {
+ fprintf(stderr, "Out of memory when adding a new pid_fd\n");
+ exit(1);
+ }
+ tmp[pid_fds_count].pid = pid;
+ tmp[pid_fds_count].fd = fd;
+ pid_fds_count++;
+ pid_fds = tmp;
+}
+
+static void gnutv_data_free_pid_fds()
+{
+ if (pid_fds_count) {
+ int i;
+ for(i=0; i< pid_fds_count; i++) {
+ close(pid_fds[i].fd);
+ }
+ }
+ if (pid_fds)
+ free(pid_fds);
+
+ pid_fds_count = 0;
+ pid_fds = NULL;
+}
diff --git a/util/gnutv/gnutv_data.h b/util/gnutv/gnutv_data.h
new file mode 100644
index 0000000..8e47e3a
--- /dev/null
+++ b/util/gnutv/gnutv_data.h
@@ -0,0 +1,39 @@
+/*
+ gnutv utility
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef gnutv_DATA_H
+#define gnutv_DATA_H 1
+
+#include <netdb.h>
+
+extern void gnutv_data_start(int output_type,
+ int ffaudiofd, int adapter_id, int demux_id,
+ char *outfile,
+ char* outif, struct addrinfo *outaddrs, int usertp);
+extern void gnutv_data_stop(void);
+
+extern void gnutv_data_new_pat(int pmt_pid);
+extern int gnutv_data_new_pmt(struct mpeg_pmt_section *pmt);
+
+
+
+#endif
diff --git a/util/gnutv/gnutv_dvb.c b/util/gnutv/gnutv_dvb.c
new file mode 100644
index 0000000..a903c26
--- /dev/null
+++ b/util/gnutv/gnutv_dvb.c
@@ -0,0 +1,376 @@
+/*
+ gnutv utility
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <signal.h>
+#include <pthread.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <libdvbapi/dvbdemux.h>
+#include <libucsi/section.h>
+#include <libucsi/mpeg/section.h>
+#include <libucsi/dvb/section.h>
+#include "gnutv.h"
+#include "gnutv_dvb.h"
+#include "gnutv_data.h"
+#include "gnutv_ca.h"
+
+#define FE_STATUS_PARAMS (DVBFE_INFO_LOCKSTATUS|DVBFE_INFO_SIGNAL_STRENGTH|DVBFE_INFO_BER|DVBFE_INFO_SNR|DVBFE_INFO_UNCORRECTED_BLOCKS)
+
+static int dvbthread_shutdown = 0;
+static pthread_t dvbthread;
+static int tune_state = 0;
+
+static int pat_version = -1;
+static int ca_pmt_version = -1;
+static int data_pmt_version = -1;
+
+static void *dvbthread_func(void* arg);
+
+static void process_pat(int pat_fd, struct gnutv_dvb_params *params, int *pmt_fd, struct pollfd *pollfd);
+static void process_tdt(int tdt_fd);
+static void process_pmt(int pmt_fd, struct gnutv_dvb_params *params);
+static int create_section_filter(int adapter, int demux, uint16_t pid, uint8_t table_id);
+
+
+int gnutv_dvb_start(struct gnutv_dvb_params *params)
+{
+ pthread_create(&dvbthread, NULL, dvbthread_func, (void*) params);
+ return 0;
+}
+
+void gnutv_dvb_stop(void)
+{
+ dvbthread_shutdown = 1;
+ pthread_join(dvbthread, NULL);
+}
+
+int gnutv_dvb_locked(void)
+{
+ return tune_state == 2;
+}
+
+static void *dvbthread_func(void* arg)
+{
+ int pat_fd = -1;
+ int pmt_fd = -1;
+ int tdt_fd = -1;
+ struct pollfd pollfds[3];
+
+ struct gnutv_dvb_params *params = (struct gnutv_dvb_params *) arg;
+
+ tune_state = 0;
+
+ // create PAT filter
+ if ((pat_fd = create_section_filter(params->adapter_id, params->demux_id,
+ TRANSPORT_PAT_PID, stag_mpeg_program_association)) < 0) {
+ fprintf(stderr, "Failed to create PAT section filter\n");
+ exit(1);
+ }
+ pollfds[0].fd = pat_fd;
+ pollfds[0].events = POLLIN|POLLPRI|POLLERR;
+
+ // create TDT filter
+ if ((tdt_fd = create_section_filter(params->adapter_id, params->demux_id, TRANSPORT_TDT_PID, stag_dvb_time_date)) < 0) {
+ fprintf(stderr, "Failed to create TDT section filter\n");
+ exit(1);
+ }
+ pollfds[1].fd = tdt_fd;
+ pollfds[1].events = POLLIN|POLLPRI|POLLERR;
+
+ // zero PMT filter
+ pollfds[2].fd = 0;
+ pollfds[2].events = 0;
+
+ // the DVB loop
+ while(!dvbthread_shutdown) {
+ // tune frontend + monitor lock status
+ if (tune_state == 0) {
+ // get the type of frontend
+ struct dvbfe_info result;
+ char *types;
+ memset(&result, 0, sizeof(result));
+ dvbfe_get_info(params->fe, 0, &result, DVBFE_INFO_QUERYTYPE_IMMEDIATE, 0);
+ switch(result.type) {
+ case DVBFE_TYPE_DVBS:
+ types = "DVB-S";
+ break;
+ case DVBFE_TYPE_DVBC:
+ types = "DVB-C";
+ break;
+ case DVBFE_TYPE_DVBT:
+ types = "DVB-T";
+ break;
+ case DVBFE_TYPE_ATSC:
+ types = "ATSC";
+ break;
+ default:
+ types = "Unknown";
+ }
+ fprintf(stderr, "Using frontend \"%s\", type %s\n", result.name, types);
+
+ // do we have a valid SEC configuration?
+ struct dvbsec_config *sec = NULL;
+ if (params->valid_sec)
+ sec = &params->sec;
+
+ // tune!
+ if (dvbsec_set(params->fe,
+ sec,
+ params->channel.polarization,
+ (params->channel.diseqc_switch & 0x01) ? DISEQC_SWITCH_B : DISEQC_SWITCH_A,
+ (params->channel.diseqc_switch & 0x02) ? DISEQC_SWITCH_B : DISEQC_SWITCH_A,
+ &params->channel.fe_params,
+ 0)) {
+ fprintf(stderr, "Failed to set frontend\n");
+ exit(1);
+ }
+
+ tune_state++;
+ } else if (tune_state == 1) {
+ struct dvbfe_info result;
+ memset(&result, 0, sizeof(result));
+ dvbfe_get_info(params->fe,
+ FE_STATUS_PARAMS,
+ &result,
+ DVBFE_INFO_QUERYTYPE_IMMEDIATE,
+ 0);
+
+ fprintf(stderr, "status %c%c%c%c%c | signal %04x | snr %04x | ber %08x | unc %08x | %s\r",
+ result.signal ? 'S' : ' ',
+ result.carrier ? 'C' : ' ',
+ result.viterbi ? 'V' : ' ',
+ result.sync ? 'Y' : ' ',
+ result.lock ? 'L' : ' ',
+ result.signal_strength,
+ result.snr,
+ result.ber,
+ result.ucblocks,
+ result.lock ? "FE_HAS_LOCK" : "");
+ fflush(stderr);
+
+ if (result.lock) {
+ tune_state++;
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ } else {
+ usleep(500000);
+ }
+ }
+
+ // is there SI data?
+ int count = poll(pollfds, 3, 100);
+ if (count < 0) {
+ if (errno != EINTR)
+ fprintf(stderr, "Poll error: %m\n");
+ break;
+ }
+ if (count == 0) {
+ continue;
+ }
+
+ // PAT
+ if (pollfds[0].revents & (POLLIN|POLLPRI)) {
+ process_pat(pat_fd, params, &pmt_fd, &pollfds[2]);
+ }
+
+ // TDT
+ if (pollfds[1].revents & (POLLIN|POLLPRI)) {
+ process_tdt(tdt_fd);
+ }
+
+ // PMT
+ if (pollfds[2].revents & (POLLIN|POLLPRI)) {
+ process_pmt(pmt_fd, params);
+ }
+ }
+
+ // close demuxers
+ if (pat_fd != -1)
+ close(pat_fd);
+ if (pmt_fd != -1)
+ close(pmt_fd);
+ if (tdt_fd != -1)
+ close(tdt_fd);
+
+ return 0;
+}
+
+static void process_pat(int pat_fd, struct gnutv_dvb_params *params, int *pmt_fd, struct pollfd *pollfd)
+{
+ int size;
+ uint8_t sibuf[4096];
+
+ // read the section
+ if ((size = read(pat_fd, sibuf, sizeof(sibuf))) < 0) {
+ return;
+ }
+
+ // parse section
+ struct section *section = section_codec(sibuf, size);
+ if (section == NULL) {
+ return;
+ }
+
+ // parse section_ext
+ struct section_ext *section_ext = section_ext_decode(section, 0);
+ if (section_ext == NULL) {
+ return;
+ }
+ if (pat_version == section_ext->version_number) {
+ return;
+ }
+
+ // parse PAT
+ struct mpeg_pat_section *pat = mpeg_pat_section_codec(section_ext);
+ if (pat == NULL) {
+ return;
+ }
+
+ // try and find the requested program
+ struct mpeg_pat_program *cur_program;
+ mpeg_pat_section_programs_for_each(pat, cur_program) {
+ if (cur_program->program_number == params->channel.service_id) {
+ // close old PMT fd
+ if (*pmt_fd != -1)
+ close(*pmt_fd);
+
+ // create PMT filter
+ if ((*pmt_fd = create_section_filter(params->adapter_id, params->demux_id,
+ cur_program->pid, stag_mpeg_program_map)) < 0) {
+ return;
+ }
+ pollfd->fd = *pmt_fd;
+ pollfd->events = POLLIN|POLLPRI|POLLERR;
+
+ gnutv_data_new_pat(cur_program->pid);
+
+ // we have a new PMT pid
+ data_pmt_version = -1;
+ ca_pmt_version = -1;
+ break;
+ }
+ }
+
+ // remember the PAT version
+ pat_version = section_ext->version_number;
+}
+
+static void process_tdt(int tdt_fd)
+{
+ int size;
+ uint8_t sibuf[4096];
+
+ // read the section
+ if ((size = read(tdt_fd, sibuf, sizeof(sibuf))) < 0) {
+ return;
+ }
+
+ // parse section
+ struct section *section = section_codec(sibuf, size);
+ if (section == NULL) {
+ return;
+ }
+
+ // parse TDT
+ struct dvb_tdt_section *tdt = dvb_tdt_section_codec(section);
+ if (tdt == NULL) {
+ return;
+ }
+
+ // done
+ gnutv_ca_new_dvbtime(dvbdate_to_unixtime(tdt->utc_time));
+}
+
+static void process_pmt(int pmt_fd, struct gnutv_dvb_params *params)
+{
+ int size;
+ uint8_t sibuf[4096];
+
+ // read the section
+ if ((size = read(pmt_fd, sibuf, sizeof(sibuf))) < 0) {
+ return;
+ }
+
+ // parse section
+ struct section *section = section_codec(sibuf, size);
+ if (section == NULL) {
+ return;
+ }
+
+ // parse section_ext
+ struct section_ext *section_ext = section_ext_decode(section, 0);
+ if (section_ext == NULL) {
+ return;
+ }
+ if ((section_ext->table_id_ext != params->channel.service_id) ||
+ ((section_ext->version_number == data_pmt_version) &&
+ (section_ext->version_number == ca_pmt_version))) {
+ return;
+ }
+
+ // parse PMT
+ struct mpeg_pmt_section *pmt = mpeg_pmt_section_codec(section_ext);
+ if (pmt == NULL) {
+ return;
+ }
+
+ // do data handling
+ if (section_ext->version_number != data_pmt_version) {
+ if (gnutv_data_new_pmt(pmt) == 1)
+ data_pmt_version = pmt->head.version_number;
+ }
+
+ // do ca handling
+ if (section_ext->version_number != ca_pmt_version) {
+ if (gnutv_ca_new_pmt(pmt) == 1)
+ ca_pmt_version = pmt->head.version_number;
+ }
+}
+
+static int create_section_filter(int adapter, int demux, uint16_t pid, uint8_t table_id)
+{
+ int demux_fd = -1;
+ uint8_t filter[18];
+ uint8_t mask[18];
+
+ // open the demuxer
+ if ((demux_fd = dvbdemux_open_demux(adapter, demux, 0)) < 0) {
+ return -1;
+ }
+
+ // create a section filter
+ memset(filter, 0, sizeof(filter));
+ memset(mask, 0, sizeof(mask));
+ filter[0] = table_id;
+ mask[0] = 0xFF;
+ if (dvbdemux_set_section_filter(demux_fd, pid, filter, mask, 1, 1)) {
+ close(demux_fd);
+ return -1;
+ }
+
+ // done
+ return demux_fd;
+}
diff --git a/util/gnutv/gnutv_dvb.h b/util/gnutv/gnutv_dvb.h
new file mode 100644
index 0000000..83ec086
--- /dev/null
+++ b/util/gnutv/gnutv_dvb.h
@@ -0,0 +1,44 @@
+/*
+ gnutv utility
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef gnutv_DVB_H
+#define gnutv_DVB_H 1
+
+#include <libdvbcfg/dvbcfg_zapchannel.h>
+#include <libdvbsec/dvbsec_api.h>
+
+struct gnutv_dvb_params {
+ int adapter_id;
+ int frontend_id;
+ int demux_id;
+ struct dvbcfg_zapchannel channel;
+ struct dvbsec_config sec;
+ int valid_sec;
+ int output_type;
+ struct dvbfe_handle *fe;
+};
+
+extern int gnutv_dvb_start(struct gnutv_dvb_params *params);
+extern void gnutv_dvb_stop(void);
+extern int gnutv_dvb_locked(void);
+
+#endif