src/input/mpegts/mpegts_mux.c \
src/input/mpegts/mpegts_service.c \
src/input/mpegts/mpegts_table.c \
- src/input/mpegts/psi.c \
+ src/input/mpegts/dvb_support.c \
+ src/input/mpegts/dvb_psi.c \
src/input/mpegts/tsdemux.c \
# MPEGTS EPG
die "SSL development support not found"
fi
+#
+# DVB API
+#
+check_cc_header 'linux/dvb/version' dvbapi
+
#
# Gzip
#
#define __TVH_MPEGTS_H__
#include "service.h"
-#include "src/input/mpegts/psi.h"
+#include "mpegts/dvb.h"
-#define MPEGTS_ONID_NONE 0xFFFF
-#define MPEGTS_TSID_NONE 0xFFFF
+#define MPEGTS_ONID_NONE 0xFFFF
+#define MPEGTS_TSID_NONE 0xFFFF
+#define MPEGTS_PSI_SECTION_SIZE 5000
/* Types */
typedef struct mpegts_table mpegts_table_t;
+typedef struct mpegts_psi_section mpegts_psi_section_t;
typedef struct mpegts_network mpegts_network_t;
typedef struct mpegts_mux mpegts_mux_t;
typedef struct mpegts_service mpegts_service_t;
* SI processing
* *************************************************************************/
-typedef int (*mpegts_table_callback)
+typedef int (*mpegts_table_callback_t)
( mpegts_table_t*, const uint8_t *buf, int len, int tableid );
+typedef void (*mpegts_psi_section_callback_t)
+ ( const uint8_t *tsb, size_t len, void *opaque );
+
+struct mpegts_psi_section
+{
+ int ps_offset;
+ int ps_lock;
+ uint8_t ps_data[MPEGTS_PSI_SECTION_SIZE];
+};
+
struct mpegts_table
{
/**
char *mt_name;
void *mt_opaque;
- mpegts_table_callback mt_callback;
+ mpegts_table_callback_t mt_callback;
// TODO: remind myself of what each field is for
int mt_destroyed; // Refcounting
int mt_refcount;
- psi_section_t mt_sect; // Manual reassembly
-
+ mpegts_psi_section_t mt_sect;
};
/**
mpegts_mux_t *mtf_mux;
};
+/*
+ * Assemble SI section
+ */
+void mpegts_psi_section_reassemble
+ ( mpegts_psi_section_t *ps, const uint8_t *tsb, int crc,
+ mpegts_psi_section_callback_t cb, void *opaque );
/* **************************************************************************
* Logical network
* Functions
*/
mpegts_mux_t* (*mn_create_mux)
- (mpegts_mux_t*, uint16_t onid, uint16_t tsid, void *aux);
+ (mpegts_mux_t*, uint16_t onid, uint16_t tsid, dvb_mux_conf_t *conf);
mpegts_service_t* (*mn_create_service)
(mpegts_mux_t*, uint16_t sid, uint16_t pmt_pid);
int dn_fe_type; // Frontend types for this network (FE_QPSK, etc)
#endif
+ uint32_t mn_nid; // limit scope of scanning
+
#if 0 // TODO: FIXME CONFIG
uint32_t dn_disable_pmt_monitor;
uint32_t dn_autodiscovery;
(mpegts_table_t *mt);
void mpegts_table_add
(mpegts_mux_t *mm, int tableid, int mask,
- mpegts_table_callback callback, void *opaque,
+ mpegts_table_callback_t callback, void *opaque,
const char *name, int flags, int pid);
void mpegts_table_flush_all
(mpegts_mux_t *mm);
/*
- * TV Input - Linux DVB interface - Support
+ * Tvheadend - DVB support routines and defines
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* EN 300 468 - V1.7.1
*/
-#ifndef DVB_SUPPORT_H
-#define DVB_SUPPORT_H
+#ifndef __TVH_DVB_SUPPORT_H__
+#define __TVH_DVB_SUPPORT_H__
-#define DVB_DESC_VIDEO_STREAM 0x02
-#define DVB_DESC_REGISTRATION 0x05
-#define DVB_DESC_CA 0x09
-#define DVB_DESC_LANGUAGE 0x0a
+struct mpegts_table;
+
+/* Defaults */
+
+
+/* Tables */
+
+#define DVB_PAT_BASE 0x00
+#define DVB_PAT_MASK 0x00
+
+#define DVB_PMT_BASE 0x02
+#define DVB_PMT_MASK 0xFF
+
+#define DVB_NIT_BASE 0x00
+#define DVB_NIT_MASK 0x00
+
+#define DVB_SDT_BASE 0x40
+#define DVB_SDT_MASK 0xF8
+
+#define DVB_BAT_BASE 0x48
+#define DVB_BAT_MASK 0xF8
+
+#define DVB_TELETEXT_BASE 0x2000
+
+/* Descriptors */
+
+#define DVB_DESC_VIDEO_STREAM 0x02
+#define DVB_DESC_REGISTRATION 0x05
+#define DVB_DESC_CA 0x09
+#define DVB_DESC_LANGUAGE 0x0A
/* Descriptors defined in EN 300 468 */
-#define DVB_DESC_NETWORK_NAME 0x40
-#define DVB_DESC_SERVICE_LIST 0x41
-#define DVB_DESC_SAT 0x43
-#define DVB_DESC_CABLE 0x44
-#define DVB_DESC_SHORT_EVENT 0x4d
-#define DVB_DESC_EXT_EVENT 0x4e
-#define DVB_DESC_SERVICE 0x48
-#define DVB_DESC_COMPONENT 0x50
-#define DVB_DESC_CONTENT 0x54
-#define DVB_DESC_PARENTAL_RAT 0x55
-#define DVB_DESC_TELETEXT 0x56
-#define DVB_DESC_SUBTITLE 0x59
-#define DVB_DESC_TERR 0x5a
-#define DVB_DESC_AC3 0x6a
-#define DVB_DESC_DEF_AUTHORITY 0x73
-#define DVB_DESC_CRID 0x76
-#define DVB_DESC_EAC3 0x7a
-#define DVB_DESC_AAC 0x7c
-#define DVB_DESC_LOCAL_CHAN 0x83
+#define DVB_DESC_NETWORK_NAME 0x40
+#define DVB_DESC_SERVICE_LIST 0x41
+#define DVB_DESC_SAT_DEL 0x43
+#define DVB_DESC_CABLE_DEL 0x44
+#define DVB_DESC_SHORT_EVENT 0x4D
+#define DVB_DESC_EXT_EVENT 0x4E
+#define DVB_DESC_SERVICE 0x48
+#define DVB_DESC_COMPONENT 0x50
+#define DVB_DESC_CONTENT 0x54
+#define DVB_DESC_PARENTAL_RAT 0x55
+#define DVB_DESC_TELETEXT 0x56
+#define DVB_DESC_SUBTITLE 0x59
+#define DVB_DESC_TERR_DEL 0x5A
+#define DVB_DESC_MULTI_NETWORK_NAME 0x5B
+#define DVB_DESC_AC3 0x6A
+#define DVB_DESC_DEF_AUTHORITY 0x73
+#define DVB_DESC_CRID 0x76
+#define DVB_DESC_EAC3 0x7A
+#define DVB_DESC_AAC 0x7C
+#define DVB_DESC_LOCAL_CHAN 0x83
+
+/* String Extraction */
typedef struct dvb_string_conv
{
const uint8_t* src, size_t srclen );
} dvb_string_conv_t;
-int dvb_get_string(char *dst, size_t dstlen, const uint8_t *src,
- const size_t srclen, const char *dvb_charset,
- dvb_string_conv_t *conv);
+int dvb_get_string
+ (char *dst, size_t dstlen, const uint8_t *src, const size_t srclen,
+ const char *dvb_charset, dvb_string_conv_t *conv);
-int dvb_get_string_with_len(char *dst, size_t dstlen,
- const uint8_t *buf, size_t buflen, const char *dvb_charset,
- dvb_string_conv_t *conv);
+int dvb_get_string_with_len
+ (char *dst, size_t dstlen, const uint8_t *buf, size_t buflen,
+ const char *dvb_charset, dvb_string_conv_t *conv);
+
+/* Conversion */
#define bcdtoint(i) ((((i & 0xf0) >> 4) * 10) + (i & 0x0f))
time_t dvb_convert_date(uint8_t *dvb_buf);
-const char *dvb_polarisation_to_str(int pol);
-const char *dvb_polarisation_to_str_long(int pol);
-
void atsc_utf16_to_utf8(uint8_t *src, int len, char *buf, int buflen);
+/*
+ * PSI processing
+ */
+
+#define FOREACH_DVB_LOOP0(ptr,len,off,min,inc,llen) \
+ for ( llen = (ptr[off] & 0xF) << 8 | ptr[off+1],\
+ ptr += off + 2,\
+ len -= off + 2;\
+ (llen > min);\
+ ptr += inc, llen -= inc + min )\
+ if (llen > len) return -1;\
+ else
+
+#define FOREACH_DVB_LOOP(ptr,len,off,min,llen)\
+ FOREACH_DVB_LOOP0(ptr,len,off,min,0,llen)
+
+#define FOREACH_DVB_DESC(ptr,len,off,llen,dtag,dlen) \
+ FOREACH_DVB_LOOP0(ptr,len,off,2,dlen,llen)\
+ if (!(dtag = *ptr++)) return -1;\
+ else if ((dlen = *ptr++) > llen - 2) return -1;\
+ else
+
+/* PSI descriptors */
+
+
+/* PSI table callbacks */
+
+int dvb_pat_callback
+ (struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
+int dvb_pmt_callback
+ (struct mpegts_table *mt, const uint8_t *ptr, int len, int tabelid);
+int dvb_nit_callback
+ (struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
+int dvb_bat_callback
+ (struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
+int dvb_sdt_callback
+ (struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
+int dvb_tdt_callback
+ (struct mpegts_table *mt, const uint8_t *ptr, int len, int tableid);
+
+/*
+ * Delivery systems and DVB API wrappers
+ *
+ * Note: although these are really only useful for linuxDVB, they are
+ * used in mpegts so that tsfile can be used to debug issues
+ */
+#if ENABLE_DVBAPI
+
+#include <linux/dvb/version.h>
+#include <linux/dvb/frontend.h>
+
+typedef struct dvb_frontend_parameters dvb_frontend_parameters_t;
+
+typedef enum polarisation {
+ POLARISATION_HORIZONTAL = 0x00,
+ POLARISATION_VERTICAL = 0x01,
+ POLARISATION_CIRCULAR_LEFT = 0x02,
+ POLARISATION_CIRCULAR_RIGHT = 0x03
+} polarisation_t;
+
+typedef struct dvb_mux_conf
+{
+ dvb_frontend_parameters_t dmc_fe_params;
+
+ // Additional DVB-S fields
+ polarisation_t dmc_fe_polarisation;
+ int dmc_fe_orbital_pos;
+ char dmc_fe_orbital_dir;
+#if DVB_API_VERSION >= 5
+ fe_modulation_t dmc_fe_modulation;
+ fe_delivery_system_t dmc_fe_delsys;
+ fe_rolloff_t dmc_fe_rolloff;
+#endif
+} dvb_mux_conf_t;
+
+/* conversion routines */
+const char *dvb_rolloff2str ( int rolloff );
+const char *dvb_delsys2str ( int delsys );
+const char *dvb_fec2str ( int fec );
+const char *dvb_qam2str ( int qam );
+const char *dvb_bw2str ( int bw );
+const char *dvb_mode2str ( int mode );
+const char *dvb_guard2str ( int guard );
+const char *dvb_hier2str ( int hier );
+const char *dvb_pol2str ( int pol );
+
+int dvb_str2rolloff ( const char *str );
+int dvb_str2delsys ( const char *str );
+int dvb_str2fec ( const char *str );
+int dvb_str2qam ( const char *str );
+int dvb_str2bw ( const char *str );
+int dvb_str2mode ( const char *str );
+int dvb_str2guard ( const char *str );
+int dvb_str2hier ( const char *str );
+int dvb_str2pol ( const char *str );
+
+#endif /* ENABLE_DVBAPI */
+
#endif /* DVB_SUPPORT_H */
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "tvheadend.h"
+#include "input/mpegts.h"
+#include "dvb.h"
+#include "tsdemux.h"
+#include "parsers.h"
+#include "lang_codes.h"
+
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
+#include <linux/dvb/version.h>
+#include <linux/dvb/frontend.h>
-#include "tvheadend.h"
-#include "psi.h"
-#include "dvb.h"
-#include "tsdemux.h"
-#include "parsers.h"
-#include "parsers/parser_teletext.h" // TODO: only for PID
-#include "lang_codes.h"
+static int
+psi_parse_pmt(mpegts_service_t *t, const uint8_t *ptr, int len, int chksvcid,
+ int delete);
-static void
-psi_table_add_pmt(mpegts_mux_t *dm, int pmt_pid);
+/* **************************************************************************
+ * Descriptors
+ * *************************************************************************/
+
+#if ENABLE_DVBAPI
+
+/**
+ * Tables for delivery descriptor parsing
+ */
+static const fe_code_rate_t fec_tab [16] = {
+ FEC_AUTO, FEC_1_2, FEC_2_3, FEC_3_4,
+ FEC_5_6, FEC_7_8, FEC_8_9,
+#if DVB_API_VERSION >= 5
+ FEC_3_5,
+#else
+ FEC_NONE,
+#endif
+ FEC_4_5,
+#if DVB_API_VERSION >= 5
+ FEC_9_10,
+#else
+ FEC_NONE,
+#endif
+ FEC_NONE, FEC_NONE,
+ FEC_NONE, FEC_NONE, FEC_NONE, FEC_NONE
+};
+
+/*
+ * Satellite delivery descriptor
+ */
+static mpegts_mux_t *
+dvb_desc_sat_del
+ (mpegts_mux_t *mm, uint16_t onid, uint16_t tsid,
+ const uint8_t *ptr, int len )
+{
+ int frequency, symrate;
+ dvb_mux_conf_t dmc;
+
+ /* Not enough data */
+ if(len < 11) return NULL;
+
+ /* Extract data */
+ frequency =
+ (bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 +
+ bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3])) * 10;
+ symrate =
+ bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 +
+ bcdtoint(ptr[9]) * 10 + (ptr[10] >> 4);
+ if (!frequency) {
+ tvhlog(LOG_WARNING, "nit", "dvb-s frequency error");
+ return NULL;
+ }
+ if (!symrate) {
+ tvhlog(LOG_WARNING, "nit", "dvb-s symbol rate error");
+ return NULL;
+ }
+
+ memset(&dmc, 0, sizeof(dmc));
+ dmc.dmc_fe_params.inversion = INVERSION_AUTO;
+ dmc.dmc_fe_params.frequency = frequency * 10;
+ dmc.dmc_fe_orbital_pos = bcdtoint(ptr[4]) * 100 + bcdtoint(ptr[5]);
+ dmc.dmc_fe_orbital_dir = (ptr[6] & 0x80) ? 'E' : 'W';
+ dmc.dmc_fe_polarisation = (ptr[6] >> 5) & 0x03;
+
+ dmc.dmc_fe_params.u.qpsk.symbol_rate = symrate * 100;
+ dmc.dmc_fe_params.u.qpsk.fec_inner = fec_tab[ptr[10] & 0x0f];
+
+#if DVB_API_VERSION >= 5
+ static int mtab[4] = {
+ 0, QPSK, PSK_8, QAM_16
+ };
+ static int rtab[4] = {
+ ROLLOFF_35, ROLLOFF_25, ROLLOFF_20, ROLLOFF_AUTO
+ };
+ dmc.dmc_fe_delsys = (ptr[6] & 0x4) ? SYS_DVBS2 : SYS_DVBS;
+ dmc.dmc_fe_modulation = mtab[ptr[6] & 0x3];
+ dmc.dmc_fe_rolloff = rtab[(ptr[6] >> 3) & 0x3];
+ if (dmc.dmc_fe_delsys == SYS_DVBS &&
+ dmc.dmc_fe_rolloff != ROLLOFF_35) {
+ tvhlog(LOG_WARNING, "nit", "dvb-s rolloff error");
+ return NULL;
+ }
+#endif
+
+ /* Debug */
+ const char *pol = dvb_pol2str(dmc.dmc_fe_polarisation);
+ tvhtrace("nit", " dvb-s%c pos %d%c freq %d %c sym %d fec %s"
+#if DVB_API_VERSION >= 5
+ " mod %s roff %s"
+#endif
+ ,
+ (ptr[6] & 0x4) ? '2' : ' ',
+ dmc.dmc_fe_orbital_pos, dmc.dmc_fe_orbital_dir,
+ dmc.dmc_fe_params.frequency,
+ pol ? pol[0] : 'X',
+ symrate,
+ dvb_fec2str(dmc.dmc_fe_params.u.qpsk.fec_inner),
+#if DVB_API_VERSION >= 5
+ dvb_qam2str(dmc.dmc_fe_modulation),
+ dvb_rolloff2str(dmc.dmc_fe_rolloff)
+#endif
+ );
+
+ /* Create */
+ return mm->mm_network->mn_create_mux(mm, onid, tsid, &dmc);
+}
+
+/*
+ * Cable delivery descriptor
+ */
+static mpegts_mux_t *
+dvb_desc_cable_del
+ (mpegts_mux_t *mm, uint16_t onid, uint16_t tsid,
+ const uint8_t *ptr, int len )
+{
+ int frequency, symrate;
+ dvb_mux_conf_t dmc;
+
+ static const fe_modulation_t qtab [6] = {
+ QAM_AUTO, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256
+ };
+
+ /* Not enough data */
+ if(len < 11) return NULL;
+
+ /* Extract data */
+ frequency =
+ bcdtoint(ptr[0]) * 1000000 + bcdtoint(ptr[1]) * 10000 +
+ bcdtoint(ptr[2]) * 100 + bcdtoint(ptr[3]);
+ symrate =
+ bcdtoint(ptr[7]) * 100000 + bcdtoint(ptr[8]) * 1000 +
+ bcdtoint(ptr[9]) * 10 + (ptr[10] >> 4);
+ if (!frequency) {
+ tvhlog(LOG_WARNING, "nit", "dvb-c frequency error");
+ return NULL;
+ }
+ if (!symrate) {
+ tvhlog(LOG_WARNING, "nit", "dvb-c symbol rate error");
+ return NULL;
+ }
+
+ memset(&dmc, 0, sizeof(dmc));
+ dmc.dmc_fe_params.inversion = INVERSION_AUTO;
+ dmc.dmc_fe_params.frequency = frequency * 100;
+
+ dmc.dmc_fe_params.u.qam.symbol_rate = symrate * 100;
+ if((ptr[6] & 0x0f) >= sizeof(qtab))
+ dmc.dmc_fe_params.u.qam.modulation = QAM_AUTO;
+ else
+ dmc.dmc_fe_params.u.qam.modulation = qtab[ptr[6] & 0x0f];
+ dmc.dmc_fe_params.u.qam.fec_inner = fec_tab[ptr[10] & 0x07];
+
+ /* Debug */
+ tvhtrace("nit", " dvb-c freq %d sym %d mod %s fec %s",
+ frequency,
+ symrate,
+ dvb_qam2str(dmc.dmc_fe_params.u.qam.modulation),
+ dvb_fec2str(dmc.dmc_fe_params.u.qam.fec_inner));
+
+ /* Create */
+ return mm->mm_network->mn_create_mux(mm, onid, tsid, &dmc);
+}
+
+/*
+ * Terrestrial delivery descriptor
+ */
+static mpegts_mux_t *
+dvb_desc_terr_del
+ (mpegts_mux_t *mm, uint16_t onid, uint16_t tsid,
+ const uint8_t *ptr, int len )
+{
+ static const fe_bandwidth_t btab [8] = {
+ BANDWIDTH_8_MHZ, BANDWIDTH_7_MHZ, BANDWIDTH_6_MHZ, BANDWIDTH_AUTO,
+ BANDWIDTH_AUTO, BANDWIDTH_AUTO, BANDWIDTH_AUTO, BANDWIDTH_AUTO
+ };
+ static const fe_modulation_t ctab [4] = {
+ QPSK, QAM_16, QAM_64, QAM_AUTO
+ };
+ static const fe_guard_interval_t gtab [4] = {
+ GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4
+ };
+ static const fe_transmit_mode_t ttab [4] = {
+ TRANSMISSION_MODE_2K,
+ TRANSMISSION_MODE_8K,
+#if DVB_API_VERSION >= 5
+ TRANSMISSION_MODE_4K,
+#else
+ TRANSMISSION_MODE_AUTO,
+#endif
+ TRANSMISSION_MODE_AUTO
+};
+ static const fe_hierarchy_t htab [8] = {
+ HIERARCHY_NONE, HIERARCHY_1, HIERARCHY_2, HIERARCHY_4,
+ HIERARCHY_NONE, HIERARCHY_1, HIERARCHY_2, HIERARCHY_4
+ };
+
+ int frequency;
+ dvb_mux_conf_t dmc;
+
+ /* Not enough data */
+ if (len < 11) return NULL;
+
+ /* Extract data */
+ frequency = ((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]);
+ if (!frequency) {
+ tvhlog(LOG_WARNING, "nit", "dvb-c frequency error");
+ return NULL;
+ }
+
+ memset(&dmc, 0, sizeof(dmc));
+ dmc.dmc_fe_params.frequency = frequency * 10;
+
+ dmc.dmc_fe_params.u.ofdm.bandwidth = btab[(ptr[4] >> 5) & 0x7];
+ dmc.dmc_fe_params.u.ofdm.constellation = ctab[(ptr[5] >> 6) & 0x3];
+ dmc.dmc_fe_params.u.ofdm.hierarchy_information = htab[(ptr[5] >> 3) & 0x3];
+ dmc.dmc_fe_params.u.ofdm.code_rate_HP = fec_tab[(ptr[5] + 1) & 0x7];
+ dmc.dmc_fe_params.u.ofdm.code_rate_LP = fec_tab[((ptr[6] + 1) >> 5) & 0x7];
+ dmc.dmc_fe_params.u.ofdm.guard_interval = gtab[(ptr[6] >> 3) & 0x3];
+ dmc.dmc_fe_params.u.ofdm.transmission_mode = ttab[(ptr[6] >> 1) & 0x3];
+
+ /* Debug */
+ tvhtrace("nit", " dvb-t freq %d bw %s cons %s hier %s code_rate %s %s guard %s trans %s",
+ frequency,
+ dvb_bw2str(dmc.dmc_fe_params.u.ofdm.bandwidth),
+ dvb_qam2str(dmc.dmc_fe_params.u.ofdm.constellation),
+ dvb_hier2str(dmc.dmc_fe_params.u.ofdm.hierarchy_information),
+ dvb_fec2str(dmc.dmc_fe_params.u.ofdm.code_rate_HP),
+ dvb_fec2str(dmc.dmc_fe_params.u.ofdm.code_rate_LP),
+ dvb_guard2str(dmc.dmc_fe_params.u.ofdm.guard_interval),
+ dvb_mode2str(dmc.dmc_fe_params.u.ofdm.transmission_mode));
+
+ /* Create */
+ return mm->mm_network->mn_create_mux(mm, onid, tsid, &dmc);
+}
+
+#endif /* ENABLE_DVBAPI */
+
+/* **************************************************************************
+ * Tables
+ * *************************************************************************/
/*
* PAT processing
*/
int
-psi_pat_callback
+dvb_pat_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
uint16_t sid, pid, tsid;
+ uint16_t nit_pid = 0x10;
mpegts_mux_t *mm = mt->mt_mux;
- tvhtrace("pat", "tableid %d len %d", tableid, len);
+ tvhtrace("pat", "tableid %02X len %d", tableid, len);
tvhlog_hexdump("pat", ptr, len);
/* Not enough data */
/* NIT PID */
if (sid == 0) {
- tvhtrace("pat", "NIT on PID %04X (%d)", pid, pid);
-#if TODO_FIXME
- if (pid != 0x10 && pid != 0x00)
- mpegts_table_add(mm, 0, 0, psi_nit_callback, NULL, "nit",
- TDT_CRC | TDT_QUICKREQ, pid)
-#endif
+ if (pid) {
+ tvhtrace("pat", "NIT on PID %04X (%d)", pid, pid);
+ nit_pid = pid;
+ }
/* Service */
} else if (pid) {
- int save = 0;
tvhtrace("pat", "SID %04X (%d) on PID %04X (%d)", sid, sid, pid, pid);
+#if 0
+ int save = 0;
if (mpegts_service_find(mm, sid, pid, NULL, &save))
if (save)
- psi_table_add_pmt(mm, pid);
- // TODO: FIXME: make PMT monitoring optional
+ mpegts_table_add(mm, DVB_PMT_BASE, DVB_PMT_MASK, dvb_pmt_callback,
+ NULL, "pmt", MT_CRC | MT_QUICKREQ, pid);
+#endif
}
/* Next */
ptr += 4;
len -= 4;
}
+
+ /* Install NIT monitor */
+ mpegts_table_add(mm, DVB_NIT_BASE, DVB_NIT_MASK, dvb_nit_callback,
+ NULL, "nit", MT_CRC | MT_QUICKREQ, nit_pid);
+
return 0;
}
* PMT processing
*/
-static int
-psi_pmt_callback
+int
+dvb_pmt_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
mpegts_mux_t *mm = mt->mt_mux;
mpegts_service_t *s;
- tvhtrace("pmt", "tableid %d len %d", tableid, len);
+ tvhtrace("pmt", "tableid %02X len %d", tableid, len);
tvhlog_hexdump("pmt", ptr, len);
LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link) {
return 0;
}
-static void
-psi_table_add_pmt(mpegts_mux_t *mm, int pmt_pid)
-{
- char pmtname[100];
- snprintf(pmtname, sizeof(pmtname), "PMT(%d)", pmt_pid);
- mpegts_table_add(mm, 0x2, 0xff, psi_pmt_callback, NULL, pmtname,
- MT_CRC | MT_QUICKREQ, pmt_pid);
-}
-
-#if 0
-void
-dvb_table_rem_pmt(dvb_mux_t *dm, int pmt_pid)
-{
- th_dvb_mux_instance_t *tdmi = dm->dm_current_tdmi;
- th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
- th_dvb_table_t *tdt = NULL;
- LIST_FOREACH(tdt, &dm->dm_tables, tdt_link)
- if (tdt->tdt_pid == pmt_pid && tdt->tdt_callback == dvb_pmt_callback)
- break;
- if (tdt)
- dvb_tdt_destroy(tda, tdmi, tdt);
-}
-#endif
-
-
/*
- * Section assembly
+ * NIT processing
*/
-
-static int
-psi_section_reassemble0(psi_section_t *ps, const uint8_t *data,
- int len, int start, int crc,
- section_handler_t *cb, void *opaque)
+int
+dvb_nit_callback
+ (mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
- int excess, tsize;
+ int i;
+ uint8_t dlen, dtag, stype;
+ uint16_t llen, dllen;
+ uint16_t nid, onid, tsid, sid;
+ mpegts_mux_t *mm = mt->mt_mux, *mux;
+ mpegts_network_t *mn = mm->mm_network;
+ char name[256];
+
+ tvhtrace("nit", "tableid %02X len %d", tableid, len);
+ tvhlog_hexdump("nit", ptr, len);
+
+ /* Not long enough */
+ if (len < 7)
+ return -1;
- if(start) {
- // Payload unit start indicator
- ps->ps_offset = 0;
- ps->ps_lock = 1;
- }
+ /* Ignore "next" */
+ if (!(ptr[2] & 0x01))
+ return -1;
- if(!ps->ps_lock)
+ /* Specific NID */
+ nid = (ptr[0] << 8) | ptr[1];
+ if (mn->mn_nid) {
+ if (mn->mn_nid != nid)
+ return -1;
+
+ /* Only use "this" network */
+ } else if (tableid != 0x40) {
return -1;
+ }
- memcpy(ps->ps_data + ps->ps_offset, data, len);
- ps->ps_offset += len;
+ /* Network Descriptors */
+ *name = 0;
+ FOREACH_DVB_DESC(ptr, len, 5, llen, dtag, dlen) {
+ tvhtrace("nit", " dtag %02X dlen %d", dtag, dlen);
- if(ps->ps_offset < 3) {
- /* We don't know the total length yet */
- return len;
+ switch (dtag) {
+ case DVB_DESC_NETWORK_NAME:
+ if (dvb_get_string(name, sizeof(name), ptr, dlen, NULL, NULL))
+ return -1;
+ break;
+ case DVB_DESC_MULTI_NETWORK_NAME:
+ // TODO: implement this?
+ break;
+ }
}
-
- tsize = 3 + (((ps->ps_data[1] & 0xf) << 8) | ps->ps_data[2]);
-
- if(ps->ps_offset < tsize)
- return len; // Not there yet
- excess = ps->ps_offset - tsize;
+ tvhtrace("nit", "network %04X (%d) [%s]", nid, nid, name);
+ // TODO: set network name
- if(crc && tvh_crc32(ps->ps_data, tsize, 0xffffffff))
- return -1;
+ /* Transport length */
+ FOREACH_DVB_LOOP(ptr, len, 0, 6, llen) {
+ mux = NULL;
+ tsid = (ptr[0] << 8) | ptr[1];
+ onid = (ptr[2] << 8) | ptr[3];
- ps->ps_offset = 0;
- if (cb)
- cb(ps->ps_data, tsize - (crc ? 4 : 0), opaque);
- return len - excess;
-}
+ tvhtrace("nit", " onid %04X (%d) tsid %04X (%d)", onid, onid, tsid, tsid);
+ FOREACH_DVB_DESC(ptr, len, 4, dllen, dtag, dlen) {
+ tvhtrace("nit", " dtag %02X dlen %d", dtag, dlen);
+ //tvhlog_hexdump("nit", ptr, dlen);
-/**
- *
- */
-void
-psi_section_reassemble(psi_section_t *ps, const uint8_t *tsb, int crc,
- section_handler_t *cb, void *opaque)
-{
- int off = tsb[3] & 0x20 ? tsb[4] + 5 : 4;
- int pusi = tsb[1] & 0x40;
- int r;
-
- if(off >= 188) {
- ps->ps_lock = 0;
- return;
- }
-
- if(pusi) {
- int len = tsb[off++];
- if(len > 0) {
- if(len > 188 - off) {
- ps->ps_lock = 0;
- return;
+ switch (dtag) {
+ case DVB_DESC_SAT_DEL:
+ mux = dvb_desc_sat_del(mm, onid, tsid, ptr, dlen);
+ break;
+ case DVB_DESC_CABLE_DEL:
+ mux = dvb_desc_cable_del(mm, onid, tsid, ptr, dlen);
+ break;
+ case DVB_DESC_TERR_DEL:
+ mux = dvb_desc_terr_del(mm, onid, tsid, ptr, dlen);
+ break;
+ case DVB_DESC_LOCAL_CHAN:
+ break;
+ case DVB_DESC_SERVICE_LIST:
+ for (i = 0; i < dlen; i += 3) {
+ sid = (ptr[i] << 8) | ptr[i+1];
+ stype = ptr[i+2];
+ tvhtrace("nit", " service %04X (%d) type %d", sid, sid, stype);
+ if (mux)
+ mux->mm_network->mn_create_service(mux, sid, 0);
+ }
+ break;
}
- psi_section_reassemble0(ps, tsb + off, len, 0, crc, cb, opaque);
- off += len;
}
}
- while(off < 188) {
- r = psi_section_reassemble0(ps, tsb + off, 188 - off, pusi, crc,
- cb, opaque);
- if(r < 0) {
- ps->ps_lock = 0;
- break;
- }
- off += r;
- pusi = 0;
- }
+ return 0;
}
/**
// We put the teletext subtitle driven streams on a list of pids
// higher than normal MPEG TS (0x2000 ++)
- int pid = PID_TELETEXT_BASE + page;
+ int pid = DVB_TELETEXT_BASE + page;
if((st = service_stream_find((service_t*)t, pid)) == NULL) {
r |= PMT_UPDATE_NEW_STREAM;
return 0;
}
+#if 0
/**
* Store service settings into message
*/
}
sort_pids(t);
}
+#endif
/*
- * TV Input - DVB - Support functions
+ * TV Input - DVB - Support/Conversion functions
* Copyright (C) 2007 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <pthread.h>
-
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/dvb/frontend.h>
#include "tvheadend.h"
-#include "dvb_support.h"
#include "dvb.h"
#include "dvb_charset_tables.h"
*/
int
-dvb_get_string(char *dst, size_t dstlen, const uint8_t *src, size_t srclen, const char *dvb_charset, dvb_string_conv_t *conv)
+dvb_get_string
+ (char *dst, size_t dstlen, const uint8_t *src, size_t srclen,
+ const char *dvb_charset, dvb_string_conv_t *conv)
{
int ic;
size_t len, outlen;
*buf = 0;
}
-
-
-
-
/*
* DVB time and date functions
*/
return (timegm(&dvb_time));
}
-/**
- *
+/*
+ * DVB API helpers
*/
-static struct strtab adaptertype[] = {
- { "DVB-S", FE_QPSK },
- { "DVB-C", FE_QAM },
- { "DVB-T", FE_OFDM },
- { "ATSC", FE_ATSC },
+#if ENABLE_DVBAPI
+
+#define dvb_str2val(p)\
+const char *dvb_##p##2str (int p) { return val2str(p, p##tab); }\
+int dvb_str2##p (const char *p) { return str2val(p, p##tab); }
+
+static struct strtab rollofftab[] = {
+#if DVB_API_VERSION >= 5
+ { "ROLLOFF_35", ROLLOFF_35 },
+ { "ROLLOFF_20", ROLLOFF_20 },
+ { "ROLLOFF_25", ROLLOFF_25 },
+ { "ROLLOFF_AUTO", ROLLOFF_AUTO }
+#endif
};
+dvb_str2val(rolloff);
+
+static struct strtab delsystab[] = {
+#if DVB_API_VERSION >= 5
+ { "SYS_UNDEFINED", SYS_UNDEFINED },
+ { "SYS_DVBC_ANNEX_AC", SYS_DVBC_ANNEX_AC },
+ { "SYS_DVBC_ANNEX_B", SYS_DVBC_ANNEX_B },
+ { "SYS_DVBT", SYS_DVBT },
+ { "SYS_DSS", SYS_DSS },
+ { "SYS_DVBS", SYS_DVBS },
+ { "SYS_DVBS2", SYS_DVBS2 },
+ { "SYS_DVBH", SYS_DVBH },
+ { "SYS_ISDBT", SYS_ISDBT },
+ { "SYS_ISDBS", SYS_ISDBS },
+ { "SYS_ISDBC", SYS_ISDBC },
+ { "SYS_ATSC", SYS_ATSC },
+ { "SYS_ATSCMH", SYS_ATSCMH },
+ { "SYS_DMBTH", SYS_DMBTH },
+ { "SYS_CMMB", SYS_CMMB },
+ { "SYS_DAB", SYS_DAB }
+#endif
+};
+dvb_str2val(delsys);
+
+static struct strtab fectab[] = {
+ { "NONE", FEC_NONE },
+ { "1/2", FEC_1_2 },
+ { "2/3", FEC_2_3 },
+ { "3/4", FEC_3_4 },
+ { "4/5", FEC_4_5 },
+ { "5/6", FEC_5_6 },
+ { "6/7", FEC_6_7 },
+ { "7/8", FEC_7_8 },
+ { "8/9", FEC_8_9 },
+ { "AUTO", FEC_AUTO },
+#if DVB_API_VERSION >= 5
+ { "3/5", FEC_3_5 },
+ { "9/10", FEC_9_10 }
+#endif
+};
+dvb_str2val(fec);
+
+static struct strtab qamtab[] = {
+ { "QPSK", QPSK },
+ { "QAM16", QAM_16 },
+ { "QAM32", QAM_32 },
+ { "QAM64", QAM_64 },
+ { "QAM128", QAM_128 },
+ { "QAM256", QAM_256 },
+ { "AUTO", QAM_AUTO },
+ { "8VSB", VSB_8 },
+ { "16VSB", VSB_16 },
+#if DVB_API_VERSION >= 5
+ { "PSK_8", PSK_8 },
+ { "APSK_16", APSK_16 },
+ { "APSK_32", APSK_32 },
+ { "DQPSK", DQPSK }
+#endif
+};
+dvb_str2val(qam);
+
+static struct strtab bwtab[] = {
+ { "8MHz", BANDWIDTH_8_MHZ },
+ { "7MHz", BANDWIDTH_7_MHZ },
+ { "6MHz", BANDWIDTH_6_MHZ },
+ { "AUTO", BANDWIDTH_AUTO },
+#if DVB_API_VERSION >= 5
+ { "5MHz", BANDWIDTH_5_MHZ },
+ { "10MHz", BANDWIDTH_10_MHZ },
+ { "1712kHz", BANDWIDTH_1_712_MHZ},
+#endif
+};
+dvb_str2val(bw);
+
+static struct strtab modetab[] = {
+ { "2k", TRANSMISSION_MODE_2K },
+ { "8k", TRANSMISSION_MODE_8K },
+ { "AUTO", TRANSMISSION_MODE_AUTO },
+#if DVB_API_VERSION >= 5
+ { "1k", TRANSMISSION_MODE_1K },
+ { "2k", TRANSMISSION_MODE_16K },
+ { "32k", TRANSMISSION_MODE_32K },
+#endif
+};
+dvb_str2val(mode);
+
+static struct strtab guardtab[] = {
+ { "1/32", GUARD_INTERVAL_1_32 },
+ { "1/16", GUARD_INTERVAL_1_16 },
+ { "1/8", GUARD_INTERVAL_1_8 },
+ { "1/4", GUARD_INTERVAL_1_4 },
+ { "AUTO", GUARD_INTERVAL_AUTO },
+#if DVB_API_VERSION >= 5
+ { "1/128", GUARD_INTERVAL_1_128 },
+ { "19/128", GUARD_INTERVAL_19_128 },
+ { "19/256", GUARD_INTERVAL_19_256},
+#endif
+};
+dvb_str2val(guard);
+
+static struct strtab hiertab[] = {
+ { "NONE", HIERARCHY_NONE },
+ { "1", HIERARCHY_1 },
+ { "2", HIERARCHY_2 },
+ { "4", HIERARCHY_4 },
+ { "AUTO", HIERARCHY_AUTO }
+};
+dvb_str2val(hier);
+static struct strtab poltab[] = {
+ { "Vertical", POLARISATION_VERTICAL },
+ { "Horizontal", POLARISATION_HORIZONTAL },
+ { "Left", POLARISATION_CIRCULAR_LEFT },
+ { "Right", POLARISATION_CIRCULAR_RIGHT },
+};
+dvb_str2val(pol);
-int
-dvb_str_to_adaptertype(const char *str)
-{
- return str2val(str, adaptertype);
-}
-
-const char *
-dvb_adaptertype_to_str(int type)
-{
- return val2str(type, adaptertype) ?: "invalid";
-}
-
-const char *
-dvb_polarisation_to_str(int pol)
-{
- switch(pol) {
- case POLARISATION_VERTICAL: return "V";
- case POLARISATION_HORIZONTAL: return "H";
- case POLARISATION_CIRCULAR_LEFT: return "L";
- case POLARISATION_CIRCULAR_RIGHT: return "R";
- default: return "X";
- }
-}
-
-const char *
-dvb_polarisation_to_str_long(int pol)
-{
- switch(pol) {
- case POLARISATION_VERTICAL: return "Vertical";
- case POLARISATION_HORIZONTAL: return "Horizontal";
- case POLARISATION_CIRCULAR_LEFT: return "Left";
- case POLARISATION_CIRCULAR_RIGHT: return "Right";
- default: return "??";
- }
-}
-
-
-/**
- *
- */
-static void
-nicenum(char *x, size_t siz, unsigned int v, const char *postfix)
-{
- if(v < 1000)
- snprintf(x, siz, "%d%s", v, postfix);
- else if(v < 1000000)
- snprintf(x, siz, "%d,%03d%s", v / 1000, v % 1000, postfix);
- else if(v < 1000000000)
- snprintf(x, siz, "%d,%03d,%03d%s",
- v / 1000000, (v % 1000000) / 1000, v % 1000, postfix);
- else
- snprintf(x, siz, "%d,%03d,%03d,%03d%s",
- v / 1000000000, (v % 1000000000) / 1000000,
- (v % 1000000) / 1000, v % 1000, postfix);
-}
-
-
-/**
- *
- */
-const char *
-dvb_mux_nicefreq(const dvb_mux_t *dm)
-{
- static char ret[100];
- int f = dm->dm_conf.dmc_fe_params.frequency;
- nicenum(ret, sizeof(ret), dm->dm_dn->dn_fe_type == FE_QPSK ? f : f / 1000,
- " kHz");
- return ret;
-}
-
-
-/**
- *
- */
-const char *
-dvb_mux_nicename(const dvb_mux_t *dm)
-{
- static char ret[100];
- const char *n = dm->dm_network_name;
-
- snprintf(ret, sizeof(ret), "%s%s%s%s%s",
- n ?: "",
- n ? ": " : "",
- dvb_mux_nicefreq(dm),
- dm->dm_dn->dn_fe_type == FE_QPSK ? " " : "",
- dm->dm_dn->dn_fe_type == FE_QPSK ?
- dvb_polarisation_to_str_long(dm->dm_conf.dmc_polarisation) :
- "");
- return ret;
-}
+#undef dvb_str2val
+#endif /* ENABLE_DVBAPI */
for (i = 0; i < len; i++) {
mt = vec[i];
if (!mt->mt_destroyed && mt->mt_pid == pid)
- psi_section_reassemble(&mt->mt_sect, mtf->mtf_tsb, 0,
- mpegts_table_dispatch, mt);
+ mpegts_psi_section_reassemble(&mt->mt_sect, mtf->mtf_tsb, 0,
+ mpegts_table_dispatch, mt);
mpegts_table_release(mt);
}
}
static mpegts_mux_t *
mpegts_network_create_mux
- ( mpegts_mux_t *mm, uint16_t sid, uint16_t tsid, void *aux )
+ ( mpegts_mux_t *mm, uint16_t sid, uint16_t tsid, dvb_mux_conf_t *aux )
{
return NULL;
}
void
mpegts_table_add
( mpegts_mux_t *mm, int tableid, int mask,
- mpegts_table_callback callback, void *opaque,
+ mpegts_table_callback_t callback, void *opaque,
const char *name, int flags, int pid )
{
mpegts_table_t *mt;
mpegts_table_destroy(mt);
}
+/*
+ * Section assembly
+ */
+static int
+mpegts_psi_section_reassemble0
+ ( mpegts_psi_section_t *ps, const uint8_t *data,
+ int len, int start, int crc,
+ mpegts_psi_section_callback_t cb, void *opaque)
+{
+ int excess, tsize;
+
+ if(start) {
+ // Payload unit start indicator
+ ps->ps_offset = 0;
+ ps->ps_lock = 1;
+ }
+
+ if(!ps->ps_lock)
+ return -1;
+
+ memcpy(ps->ps_data + ps->ps_offset, data, len);
+ ps->ps_offset += len;
+
+ if(ps->ps_offset < 3) {
+ /* We don't know the total length yet */
+ return len;
+ }
+
+ tsize = 3 + (((ps->ps_data[1] & 0xf) << 8) | ps->ps_data[2]);
+
+ if(ps->ps_offset < tsize)
+ return len; // Not there yet
+
+ excess = ps->ps_offset - tsize;
+
+ if(crc && tvh_crc32(ps->ps_data, tsize, 0xffffffff))
+ return -1;
+
+ ps->ps_offset = 0;
+ if (cb)
+ cb(ps->ps_data, tsize - (crc ? 4 : 0), opaque);
+ return len - excess;
+}
+
+
+/**
+ *
+ */
+void
+mpegts_psi_section_reassemble
+ (mpegts_psi_section_t *ps, const uint8_t *tsb, int crc,
+ mpegts_psi_section_callback_t cb, void *opaque)
+{
+ int off = tsb[3] & 0x20 ? tsb[4] + 5 : 4;
+ int pusi = tsb[1] & 0x40;
+ int r;
+
+ if(off >= 188) {
+ ps->ps_lock = 0;
+ return;
+ }
+
+ if(pusi) {
+ int len = tsb[off++];
+ if(len > 0) {
+ if(len > 188 - off) {
+ ps->ps_lock = 0;
+ return;
+ }
+ mpegts_psi_section_reassemble0(ps, tsb + off, len, 0, crc, cb, opaque);
+ off += len;
+ }
+ }
+
+ while(off < 188) {
+ r = mpegts_psi_section_reassemble0(ps, tsb + off, 188 - off, pusi, crc,
+ cb, opaque);
+ if(r < 0) {
+ ps->ps_lock = 0;
+ break;
+ }
+ off += r;
+ pusi = 0;
+ }
+}
+
+
+
/******************************************************************************
* Editor Configuration
*
struct mpegts_service;
struct mpegts_table;
-typedef void (section_handler_t)(const uint8_t *data, size_t len, void *opaque);
-
-typedef struct psi_section {
- int ps_offset;
- int ps_lock;
- uint8_t ps_data[PSI_SECTION_SIZE];
-} psi_section_t;
-
-
-void psi_section_reassemble(psi_section_t *ps, const uint8_t *tsb, int crc,
- section_handler_t *cb, void *opaque);
-
int psi_parse_pmt
(struct mpegts_service *t, const uint8_t *ptr, int len, int chksvcid,
int delete);
/**
* Code for dealing with a complete section
*/
+#if TODO_MOVE_THIS
static void
got_section(const uint8_t *data, size_t len, void *opaque)
{
#endif
}
}
+#endif
/**
case SCT_CA:
case SCT_PMT:
+#if TODO_MOVE_THIS
+ break; // TODO: we should not receive these
if(st->es_section == NULL)
st->es_section = calloc(1, sizeof(struct psi_section));
psi_section_reassemble(st->es_section, tsb, st->es_section_docrc,
got_section, st);
+#endif
break;
default:
#include "tvheadend.h"
#include "tsfile_private.h"
#include "input.h"
-#include "input/mpegts/psi.h"
+#include "input/mpegts/dvb.h"
#include <sys/epoll.h>
#include <sys/types.h>
LIST_INSERT_HEAD(&mi->mi_mux_active, t, mmi_active_link);
/* Install table handlers */
- mpegts_table_add(mm, 0x0, 0xff, psi_pat_callback, NULL, "pat",
- MT_QUICKREQ| MT_CRC, 0);
+ mpegts_table_add(mm, DVB_PAT_BASE, DVB_PAT_MASK, dvb_pat_callback,
+ NULL, "pat", MT_QUICKREQ| MT_CRC, 0);
#if 0
mpegts_table_add(mm, 0x1, 0xff, dvb_cat_callback, NULL, "cat",
MT_CRC, 1);
int es_demuxer_fd;
int es_peak_presentation_delay; /* Max seen diff. of DTS and PTS */
- struct psi_section *es_section;
- int es_section_docrc; /* Set if we should verify CRC on tables */
-#ifdef TODO_CAN_THIS_BE_REMOVED
- pid_section_callback_t *es_got_section;
-#endif
- void *es_got_section_opaque;
-
/* PCR recovery */
int es_pcr_recovery_fails;
struct in6_addr s_iptv_group6;
uint16_t s_iptv_port;
int s_iptv_fd;
-
- /**
- * For per-transport PAT/PMT parsers, allocated on demand
- * Free'd by transport_destroy
- */
- struct psi_section *s_pat_section;
- struct psi_section *s_pmt_section;
- // Note: are the above still required!
#endif
/**