root/trunk/tvheadend/src/dvb/dvb_multiplex.c @ 2544

Revision 2544, 12.6 KB (checked in by andoma, 18 months ago)

Make tvheadend stand on its own configure and build system.

Line 
1/*
2 *  TV Input - Linux DVB interface
3 *  Copyright (C) 2007 Andreas �an
4 *
5 *  This program is free software: you can redistribute it and/or modify
6 *  it under the terms of the GNU General Public License as published by
7 *  the Free Software Foundation, either version 3 of the License, or
8 *  (at your option) any later version.
9 *
10 *  This program is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *  GNU General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <pthread.h>
20#include <assert.h>
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <sys/ioctl.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <ctype.h>
28#include <stdio.h>
29#include <unistd.h>
30#include <stdlib.h>
31#include <string.h>
32#include <dirent.h>
33
34#include <linux/dvb/frontend.h>
35#include <linux/dvb/dmx.h>
36
37#include "settings.h"
38
39#include "tvhead.h"
40#include "dvb.h"
41#include "channels.h"
42#include "transports.h"
43#include "teletext.h"
44#include "psi.h"
45#include "dvb_support.h"
46#include "notify.h"
47
48struct th_dvb_mux_instance_tree dvb_muxes;
49
50static struct strtab muxfestatustab[] = {
51  { "Unknown",      TDMI_FE_UNKNOWN },
52  { "No signal",    TDMI_FE_NO_SIGNAL },
53  { "Faint signal", TDMI_FE_FAINT_SIGNAL },
54  { "Bad signal",   TDMI_FE_BAD_SIGNAL },
55  { "Constant FEC", TDMI_FE_CONSTANT_FEC },
56  { "Bursty FEC",   TDMI_FE_BURSTY_FEC },
57  { "OK",           TDMI_FE_OK },
58};
59
60
61/**
62 *  Return a readable status text for the given mux
63 */
64const char *
65dvb_mux_status(th_dvb_mux_instance_t *tdmi)
66{
67  return val2str(tdmi->tdmi_fe_status, muxfestatustab) ?: "Invalid";
68}
69
70
71
72
73/**
74 *
75 */
76static int
77tdmi_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b)
78{
79  if(a->tdmi_switchport != b->tdmi_switchport)
80    return a->tdmi_switchport - b->tdmi_switchport;
81
82  if(a->tdmi_fe_params.frequency != b->tdmi_fe_params.frequency)
83    return a->tdmi_fe_params.frequency - b->tdmi_fe_params.frequency;
84
85  return a->tdmi_polarisation - b->tdmi_polarisation;
86}
87
88/**
89 *
90 */
91static int
92tdmi_global_cmp(th_dvb_mux_instance_t *a, th_dvb_mux_instance_t *b)
93{
94  return strcmp(a->tdmi_identifier, b->tdmi_identifier);
95}
96
97
98/**
99 * Create a new mux on the given adapter, return NULL if it already exists
100 */
101th_dvb_mux_instance_t *
102dvb_mux_create(th_dvb_adapter_t *tda, struct dvb_frontend_parameters *fe_param,
103               int polarisation, int switchport,
104               uint16_t tsid, const char *network, const char *source)
105{
106  th_dvb_mux_instance_t *tdmi;
107  static th_dvb_mux_instance_t *skel;
108  char buf[200];
109  char qpsktxt[20];
110  int entries_before = tda->tda_muxes.entries;
111
112  lock_assert(&global_lock);
113
114  if(skel == NULL)
115    skel = calloc(1, sizeof(th_dvb_mux_instance_t));
116
117  skel->tdmi_polarisation = polarisation;
118  skel->tdmi_switchport = switchport;
119  skel->tdmi_fe_params.frequency = fe_param->frequency;
120
121  tdmi = RB_INSERT_SORTED(&tda->tda_muxes, skel, tdmi_adapter_link, tdmi_cmp);
122  if(tdmi != NULL)
123    return NULL;
124
125  tdmi = skel;
126  skel = NULL;
127
128  tdmi->tdmi_scan_queue = &tda->tda_scan_queues[DVB_MUX_SCAN_INITIAL];
129  TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
130
131  tdmi->tdmi_transport_stream_id = tsid;
132  tdmi->tdmi_adapter = tda;
133  tdmi->tdmi_network = network ? strdup(network) : NULL;
134  tdmi->tdmi_quality = 100;
135
136  if(entries_before == 0 && tda->tda_rootpath != NULL) {
137    /* First mux on adapter with backing hardware, start scanner */
138    gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1);
139  }
140
141  memcpy(&tdmi->tdmi_fe_params, fe_param,
142         sizeof(struct dvb_frontend_parameters));
143
144  if(tda->tda_type == FE_QPSK)
145    snprintf(qpsktxt, sizeof(qpsktxt), "_%s_%d",
146             dvb_polarisation_to_str(polarisation), switchport);
147  else
148    qpsktxt[0] = 0;
149
150  snprintf(buf, sizeof(buf), "%s%d%s",
151           tda->tda_identifier,fe_param->frequency, qpsktxt);
152
153  tdmi->tdmi_identifier = strdup(buf);
154
155  RB_INSERT_SORTED(&dvb_muxes, tdmi, tdmi_global_link, tdmi_global_cmp);
156
157  if(source != NULL) {
158    dvb_mux_nicename(buf, sizeof(buf), tdmi);
159    tvhlog(LOG_NOTICE, "dvb", "New mux \"%s\" created by %s", buf, source);
160
161    dvb_mux_save(tdmi);
162    dvb_adapter_notify_reload(tda);
163  }
164
165  dvb_transport_load(tdmi);
166
167  return tdmi;
168}
169
170/**
171 * Destroy a DVB mux (it might come back by itself very soon though :)
172 */
173void
174dvb_mux_destroy(th_dvb_mux_instance_t *tdmi)
175{
176  th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
177  th_transport_t *t;
178
179  lock_assert(&global_lock);
180 
181  hts_settings_remove("dvbmuxes/%s/%s",
182                      tda->tda_identifier, tdmi->tdmi_identifier); 
183
184  while((t = LIST_FIRST(&tdmi->tdmi_transports)) != NULL)
185    transport_destroy(t);
186
187  if(tda->tda_mux_current == tdmi)
188    dvb_fe_stop(tda->tda_mux_current);
189
190  RB_REMOVE(&dvb_muxes, tdmi, tdmi_global_link);
191  RB_REMOVE(&tda->tda_muxes, tdmi, tdmi_adapter_link);
192
193  if(tdmi->tdmi_scan_queue != NULL)
194    TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
195
196  hts_settings_remove("dvbmuxes/%s", tdmi->tdmi_identifier);
197
198  free(tdmi->tdmi_network);
199  free(tdmi->tdmi_identifier);
200  free(tdmi);
201}
202
203
204/**
205 *
206 */
207th_dvb_mux_instance_t *
208dvb_mux_find_by_identifier(const char *identifier)
209{
210  th_dvb_mux_instance_t skel;
211
212  lock_assert(&global_lock);
213
214  skel.tdmi_identifier = (char *)identifier;
215  return RB_FIND(&dvb_muxes, &skel, tdmi_global_link, tdmi_global_cmp);
216}
217
218
219
220
221
222
223static struct strtab fectab[] = {
224  { "NONE", FEC_NONE },
225  { "1/2",  FEC_1_2 },
226  { "2/3",  FEC_2_3 },
227  { "3/4",  FEC_3_4 },
228  { "4/5",  FEC_4_5 },
229  { "5/6",  FEC_5_6 },
230  { "6/7",  FEC_6_7 },
231  { "7/8",  FEC_7_8 },
232  { "8/9",  FEC_8_9 },
233  { "AUTO", FEC_AUTO }
234};
235
236static struct strtab qamtab[] = {
237  { "QPSK",   QPSK },
238  { "QAM16",  QAM_16 },
239  { "QAM32",  QAM_32 },
240  { "QAM64",  QAM_64 },
241  { "QAM128", QAM_128 },
242  { "QAM256", QAM_256 },
243  { "AUTO",   QAM_AUTO },
244  { "8VSB",   VSB_8 },
245  { "16VSB",  VSB_16 }
246};
247
248static struct strtab bwtab[] = {
249  { "8MHz", BANDWIDTH_8_MHZ },
250  { "7MHz", BANDWIDTH_7_MHZ },
251  { "6MHz", BANDWIDTH_6_MHZ },
252  { "AUTO", BANDWIDTH_AUTO }
253};
254
255static struct strtab modetab[] = {
256  { "2k",   TRANSMISSION_MODE_2K },
257  { "8k",   TRANSMISSION_MODE_8K },
258  { "AUTO", TRANSMISSION_MODE_AUTO }
259};
260
261static struct strtab guardtab[] = {
262  { "1/32", GUARD_INTERVAL_1_32 },
263  { "1/16", GUARD_INTERVAL_1_16 },
264  { "1/8",  GUARD_INTERVAL_1_8 },
265  { "1/4",  GUARD_INTERVAL_1_4 },
266  { "AUTO", GUARD_INTERVAL_AUTO },
267};
268
269static struct strtab hiertab[] = {
270  { "NONE", HIERARCHY_NONE },
271  { "1",    HIERARCHY_1 },
272  { "2",    HIERARCHY_2 },
273  { "4",    HIERARCHY_4 },
274  { "AUTO", HIERARCHY_AUTO }
275};
276
277static struct strtab poltab[] = {
278  { "Vertical",      POLARISATION_VERTICAL },
279  { "Horizontal",    POLARISATION_HORIZONTAL },
280};
281
282
283/**
284 *
285 */
286void
287dvb_mux_save(th_dvb_mux_instance_t *tdmi)
288{
289  struct dvb_frontend_parameters *f = &tdmi->tdmi_fe_params;
290
291  htsmsg_t *m = htsmsg_create_map();
292
293  htsmsg_add_u32(m, "quality", tdmi->tdmi_quality);
294  htsmsg_add_str(m, "status", dvb_mux_status(tdmi));
295
296  htsmsg_add_u32(m, "transportstreamid", tdmi->tdmi_transport_stream_id);
297  if(tdmi->tdmi_network != NULL)
298    htsmsg_add_str(m, "network", tdmi->tdmi_network);
299
300  htsmsg_add_u32(m, "frequency", f->frequency);
301
302  switch(tdmi->tdmi_adapter->tda_type) {
303  case FE_OFDM:
304    htsmsg_add_str(m, "bandwidth",
305                   val2str(f->u.ofdm.bandwidth, bwtab));
306
307    htsmsg_add_str(m, "constellation",
308            val2str(f->u.ofdm.constellation, qamtab));
309
310    htsmsg_add_str(m, "transmission_mode",
311            val2str(f->u.ofdm.transmission_mode, modetab));
312
313    htsmsg_add_str(m, "guard_interval",
314            val2str(f->u.ofdm.guard_interval, guardtab));
315
316    htsmsg_add_str(m, "hierarchy",
317            val2str(f->u.ofdm.hierarchy_information, hiertab));
318
319    htsmsg_add_str(m, "fec_hi",
320            val2str(f->u.ofdm.code_rate_HP, fectab));
321
322    htsmsg_add_str(m, "fec_lo",
323            val2str(f->u.ofdm.code_rate_LP, fectab));
324    break;
325
326  case FE_QPSK:
327    htsmsg_add_u32(m, "symbol_rate", f->u.qpsk.symbol_rate);
328
329    htsmsg_add_str(m, "fec",
330            val2str(f->u.qpsk.fec_inner, fectab));
331
332    htsmsg_add_str(m, "polarisation",
333            val2str(tdmi->tdmi_polarisation, poltab));
334 
335    htsmsg_add_u32(m, "switchport", tdmi->tdmi_switchport);
336    break;
337
338  case FE_QAM:
339    htsmsg_add_u32(m, "symbol_rate", f->u.qam.symbol_rate);
340
341    htsmsg_add_str(m, "fec",
342            val2str(f->u.qam.fec_inner, fectab));
343
344    htsmsg_add_str(m, "constellation",
345            val2str(f->u.qam.modulation, qamtab));
346    break;
347
348  case FE_ATSC:
349    break;
350  }
351
352  hts_settings_save(m, "dvbmuxes/%s/%s",
353                    tdmi->tdmi_adapter->tda_identifier, tdmi->tdmi_identifier);
354  htsmsg_destroy(m);
355}
356
357
358/**
359 *
360 */
361static const char *
362tdmi_create_by_msg(th_dvb_adapter_t *tda, htsmsg_t *m)
363{
364  th_dvb_mux_instance_t *tdmi;
365  struct dvb_frontend_parameters f;
366  const char *s;
367  int r;
368  int polarisation = 0;
369  unsigned int switchport = 0;
370  unsigned int tsid, u32;
371
372  memset(&f, 0, sizeof(f));
373 
374  f.inversion = INVERSION_AUTO;
375  htsmsg_get_u32(m, "frequency", &f.frequency);
376
377
378  switch(tda->tda_type) {
379  case FE_OFDM:
380    s = htsmsg_get_str(m, "bandwidth");
381    if(s == NULL || (r = str2val(s, bwtab)) < 0)
382      return "Invalid bandwidth";
383    f.u.ofdm.bandwidth = r;
384
385    s = htsmsg_get_str(m, "constellation");
386    if(s == NULL || (r = str2val(s, qamtab)) < 0)
387      return "Invalid QAM constellation";
388    f.u.ofdm.constellation = r;
389
390    s = htsmsg_get_str(m, "transmission_mode");
391    if(s == NULL || (r = str2val(s, modetab)) < 0)
392      return "Invalid transmission mode";
393    f.u.ofdm.transmission_mode = r;
394
395    s = htsmsg_get_str(m, "guard_interval");
396    if(s == NULL || (r = str2val(s, guardtab)) < 0)
397      return "Invalid guard interval";
398    f.u.ofdm.guard_interval = r;
399
400    s = htsmsg_get_str(m, "hierarchy");
401    if(s == NULL || (r = str2val(s, hiertab)) < 0)
402      return "Invalid heirarchy information";
403    f.u.ofdm.hierarchy_information = r;
404
405    s = htsmsg_get_str(m, "fec_hi");
406    if(s == NULL || (r = str2val(s, fectab)) < 0)
407      return "Invalid hi-FEC";
408    f.u.ofdm.code_rate_HP = r;
409
410    s = htsmsg_get_str(m, "fec_lo");
411    if(s == NULL || (r = str2val(s, fectab)) < 0)
412      return "Invalid lo-FEC";
413    f.u.ofdm.code_rate_LP = r;
414    break;
415
416  case FE_QPSK:
417    htsmsg_get_u32(m, "symbol_rate", &f.u.qpsk.symbol_rate);
418    if(f.u.qpsk.symbol_rate == 0)
419      return "Invalid symbol rate";
420   
421    s = htsmsg_get_str(m, "fec");
422    if(s == NULL || (r = str2val(s, fectab)) < 0)
423      return "Invalid FEC";
424    f.u.qpsk.fec_inner = r;
425
426    s = htsmsg_get_str(m, "polarisation");
427    if(s == NULL || (r = str2val(s, poltab)) < 0)
428      return "Invalid polarisation";
429    polarisation = r;
430   
431    htsmsg_get_u32(m, "switchport", &switchport);
432    break;
433
434  case FE_QAM:
435    htsmsg_get_u32(m, "symbol_rate", &f.u.qam.symbol_rate);
436    if(f.u.qam.symbol_rate == 0)
437      return "Invalid symbol rate";
438   
439    s = htsmsg_get_str(m, "constellation");
440    if(s == NULL || (r = str2val(s, qamtab)) < 0)
441      return "Invalid QAM constellation";
442    f.u.qam.modulation = r;
443
444    s = htsmsg_get_str(m, "fec");
445    if(s == NULL || (r = str2val(s, fectab)) < 0)
446      return "Invalid FEC";
447    f.u.qam.fec_inner = r;
448    break;
449
450  case FE_ATSC:
451    break;
452  }
453
454  if(htsmsg_get_u32(m, "transportstreamid", &tsid))
455    tsid = 0xffff;
456
457  tdmi = dvb_mux_create(tda, &f, polarisation, switchport,
458                        tsid, htsmsg_get_str(m, "network"), NULL);
459  if(tdmi != NULL) {
460
461    if((s = htsmsg_get_str(m, "status")) != NULL)
462      tdmi->tdmi_fe_status = str2val(s, muxfestatustab);
463
464    if(!htsmsg_get_u32(m, "quality", &u32)) {
465      tdmi->tdmi_quality = u32;
466
467      if(tdmi->tdmi_scan_queue != NULL)
468        TAILQ_REMOVE(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
469
470      if(tdmi->tdmi_quality == 100) {
471        tdmi->tdmi_scan_queue = &tda->tda_scan_queues[DVB_MUX_SCAN_OK];
472      } else {
473        tdmi->tdmi_scan_queue = &tda->tda_scan_queues[DVB_MUX_SCAN_BAD];
474      }
475      TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
476    }
477
478  }
479  return NULL;
480}
481
482
483
484/**
485 *
486 */
487void
488dvb_mux_load(th_dvb_adapter_t *tda)
489{
490  htsmsg_t *l, *c;
491  htsmsg_field_t *f;
492
493  if((l = hts_settings_load("dvbmuxes/%s", tda->tda_identifier)) == NULL)
494    return;
495 
496  HTSMSG_FOREACH(f, l) {
497    if((c = htsmsg_get_map_by_field(f)) == NULL)
498      continue;
499   
500    tdmi_create_by_msg(tda, c);
501  }
502  htsmsg_destroy(l);
503}
504
505/**
506 *
507 */
508void
509dvb_mux_set_networkname(th_dvb_mux_instance_t *tdmi, const char *networkname)
510{
511  htsmsg_t *m = htsmsg_create_map();
512  char buf[100];
513
514  htsmsg_add_str(m, "id", tdmi->tdmi_identifier);
515
516  free((void *)tdmi->tdmi_network);
517  tdmi->tdmi_network = strdup(networkname);
518  dvb_mux_save(tdmi);
519
520  dvb_mux_nicename(buf, sizeof(buf), tdmi);
521  htsmsg_add_str(m, "name", buf);
522  notify_by_msg("dvbmux", m);
523}
Note: See TracBrowser for help on using the browser.