Project

General

Profile

RE: UK Cable OTA EPG » mpegts_mux.c

Paul Williams, 2016-08-23 13:07

 
1
/*
2
 *  Tvheadend - MPEGTS multiplex
3
 *
4
 *  Copyright (C) 2013 Adam Sutton
5
 *
6
 *  This program is free software: you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation, either version 3 of the License, or
9
 *  (at your option) any later version.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19

20
#include "idnode.h"
21
#include "queue.h"
22
#include "input.h"
23
#include "subscriptions.h"
24
#include "streaming.h"
25
#include "channels.h"
26
#include "access.h"
27
#include "profile.h"
28
#include "dvb_charset.h"
29

30
#include <assert.h>
31

32
static void mpegts_mux_scan_timeout ( void *p );
33
static void mpegts_mux_do_stop ( mpegts_mux_t *mm, int delconf );
34

35

36
/* ****************************************************************************
37
 * Mux instance (input linkage)
38
 * ***************************************************************************/
39

40
const idclass_t mpegts_mux_instance_class =
41
{
42
  .ic_super      = &tvh_input_instance_class,
43
  .ic_class      = "mpegts_mux_instance",
44
  .ic_caption    = N_("MPEG-TS multiplex PHY"),
45
  .ic_perm_def   = ACCESS_ADMIN
46
};
47

48
void
49
mpegts_mux_instance_delete
50
  ( tvh_input_instance_t *tii )
51
{
52
  mpegts_mux_instance_t *mmi = (mpegts_mux_instance_t *)tii;
53

54
  idnode_save_check(&tii->tii_id, 1);
55
  idnode_unlink(&tii->tii_id);
56
  LIST_REMOVE(mmi, mmi_mux_link);
57
  LIST_REMOVE(tii, tii_input_link);
58
  pthread_mutex_destroy(&mmi->tii_stats_mutex);
59
  free(mmi);
60
}
61

62
mpegts_mux_instance_t *
63
mpegts_mux_instance_create0
64
  ( mpegts_mux_instance_t *mmi, const idclass_t *class, const char *uuid,
65
    mpegts_input_t *mi, mpegts_mux_t *mm )
66
{
67
  // TODO: does this need to be an idnode?
68
  if (idnode_insert(&mmi->tii_id, uuid, class, 0)) {
69
    free(mmi);
70
    return NULL;
71
  }
72

73
  pthread_mutex_init(&mmi->tii_stats_mutex, NULL);
74

75
  /* Setup links */
76
  mmi->mmi_mux   = mm;
77
  mmi->mmi_input = mi;
78
  
79
  /* Callbacks */
80
  mmi->tii_delete = mpegts_mux_instance_delete;
81
  mmi->tii_clear_stats = tvh_input_instance_clear_stats;
82

83
  LIST_INSERT_HEAD(&mm->mm_instances, mmi, mmi_mux_link);
84
  LIST_INSERT_HEAD(&mi->mi_mux_instances, (tvh_input_instance_t *)mmi, tii_input_link);
85

86

87
  return mmi;
88
}
89

90
static void
91
mpegts_mux_scan_active
92
  ( mpegts_mux_t *mm, const char *buf, mpegts_input_t *mi )
93
{
94
  int t;
95

96
  /* Setup scan */
97
  if (mm->mm_scan_state == MM_SCAN_STATE_PEND) {
98
    mpegts_network_scan_mux_active(mm);
99

100
    /* Get timeout */
101
    t = mpegts_input_grace(mi, mm);
102
  
103
    /* Setup timeout */
104
    mtimer_arm_rel(&mm->mm_scan_timeout, mpegts_mux_scan_timeout, mm, sec2mono(t));
105
  }
106
}
107

108
static int
109
mpegts_mux_keep_exists
110
  ( mpegts_input_t *mi )
111
{
112
  const mpegts_mux_instance_t *mmi;
113
  const service_t *s;
114
  const th_subscription_t *ths;
115
  int ret;
116

117
  lock_assert(&global_lock);
118

119
  if (!mi)
120
    return 0;
121

122
  ret = 0;
123
  LIST_FOREACH(mmi, &mi->mi_mux_active, mmi_active_link)
124
    LIST_FOREACH(ths, &mmi->mmi_mux->mm_raw_subs, ths_mux_link) {
125
      s = ths->ths_service;
126
      if (s && s->s_type == STYPE_RAW && !strcmp(ths->ths_title, "keep")) {
127
        ret = 1;
128
        break;
129
      }
130
    }
131
  return ret;
132
}
133

134
static int
135
mpegts_mux_subscribe_keep
136
  ( mpegts_mux_t *mm, mpegts_input_t *mi )
137
{
138
  char *s;
139
  int r;
140

141
  s = mi->mi_linked;
142
  mi->mi_linked = NULL;
143
  tvhtrace(LS_MPEGTS, "subscribe keep for '%s' (%p)", mi->mi_name, mm);
144
  r = mpegts_mux_subscribe(mm, mi, "keep", SUBSCRIPTION_PRIO_KEEP,
145
                           SUBSCRIPTION_ONESHOT | SUBSCRIPTION_MINIMAL);
146
  mi->mi_linked = s;
147
  return r;
148
}
149

150
static void
151
mpegts_mux_subscribe_linked
152
  ( mpegts_input_t *mi, mpegts_mux_t *mm )
153
{
154
  mpegts_input_t *mi2 = mpegts_input_find(mi->mi_linked);
155
  mpegts_mux_instance_t *mmi2;
156
  mpegts_network_link_t *mnl2;
157
  mpegts_mux_t *mm2;
158
  char buf1[128], buf2[128];
159
  const char *serr = "All";
160
  int r = 0;
161

162
  tvhtrace(LS_MPEGTS, "subscribe linked");
163

164
  if (!mpegts_mux_keep_exists(mi) && (r = mpegts_mux_subscribe_keep(mm, mi))) {
165
    serr = "active1";
166
    goto fatal;
167
  }
168

169
  if (!mi2)
170
    return;
171

172
  if (mpegts_mux_keep_exists(mi2))
173
    return;
174

175
  mmi2 = LIST_FIRST(&mi2->mi_mux_active);
176
  if (mmi2) {
177
    if (!mpegts_mux_subscribe_keep(mmi2->mmi_mux, mi2))
178
      return;
179
    serr = "active2";
180
    goto fatal;
181
  }
182

183
  /* Try muxes within same network */
184
  LIST_FOREACH(mnl2, &mi2->mi_networks, mnl_mi_link)
185
    if (mnl2->mnl_network == mm->mm_network)
186
      LIST_FOREACH(mm2, &mnl2->mnl_network->mn_muxes, mm_network_link)
187
        if (!mm2->mm_active && MM_SCAN_CHECK_OK(mm) &&
188
            !LIST_EMPTY(&mm2->mm_services))
189
          if (!mpegts_mux_subscribe_keep(mm2, mi2))
190
            return;
191

192
  /* Try all other muxes */
193
  LIST_FOREACH(mnl2, &mi2->mi_networks, mnl_mi_link)
194
    if (mnl2->mnl_network != mm->mm_network)
195
      LIST_FOREACH(mm2, &mnl2->mnl_network->mn_muxes, mm_network_link)
196
        if (!mm2->mm_active && MM_SCAN_CHECK_OK(mm) &&
197
            !LIST_EMPTY(&mm2->mm_services))
198
          if (!mpegts_mux_subscribe_keep(mm2, mi2))
199
            return;
200

201
fatal:
202
  mi ->mi_display_name(mi,  buf1, sizeof(buf1));
203
  mi2->mi_display_name(mi2, buf2, sizeof(buf2));
204
  tvherror(LS_MPEGTS, "%s - %s - linked input cannot be started (%s: %i)", buf1, buf2, serr, r);
205
}
206

207
void
208
mpegts_mux_unsubscribe_linked
209
  ( mpegts_input_t *mi, service_t *t )
210
{
211
  th_subscription_t *ths, *ths_next;
212

213
  if (mi) {
214
    tvhtrace(LS_MPEGTS, "unsubscribing linked");
215

216
    for (ths = LIST_FIRST(&subscriptions); ths; ths = ths_next) {
217
      ths_next = LIST_NEXT(ths, ths_global_link);
218
      if (ths->ths_source == (tvh_input_t *)mi && !strcmp(ths->ths_title, "keep") &&
219
          ths->ths_service != t)
220
        subscription_unsubscribe(ths, UNSUBSCRIBE_FINAL);
221
    }
222
  }
223
}
224

225
int
226
mpegts_mux_instance_start
227
  ( mpegts_mux_instance_t **mmiptr, service_t *t, int weight )
228
{
229
  int r;
230
  char buf[256], buf2[256];
231
  mpegts_mux_instance_t *mmi = *mmiptr;
232
  mpegts_mux_t          * mm = mmi->mmi_mux;
233
  mpegts_input_t        * mi = mmi->mmi_input;
234
  mpegts_service_t      *  s;
235
  mpegts_mux_nice_name(mm, buf, sizeof(buf));
236

237
  /* Already active */
238
  if (mm->mm_active) {
239
    *mmiptr = mm->mm_active;
240
    tvhdebug(LS_MPEGTS, "%s - already active", buf);
241
    mpegts_mux_scan_active(mm, buf, (*mmiptr)->mmi_input);
242
    return 0;
243
  }
244

245
  /* Dead service check */
246
  LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link)
247
    s->s_dvb_check_seen = s->s_dvb_last_seen;
248

249
  mm->mm_tsid_checks = 0;
250

251
  /* Start */
252
  mi->mi_display_name(mi, buf2, sizeof(buf2));
253
  tvhinfo(LS_MPEGTS, "%s - tuning on %s", buf, buf2);
254

255
  if (mi->mi_linked)
256
    mpegts_mux_unsubscribe_linked(mi, t);
257

258
  r = mi->mi_warm_mux(mi, mmi);
259
  if (r) return r;
260
  r = mi->mi_start_mux(mi, mmi, weight);
261
  if (r) return r;
262

263
  /* Start */
264
  tvhdebug(LS_MPEGTS, "%s - started", buf);
265
  mi->mi_started_mux(mi, mmi);
266

267
  /* Event handler */
268
  mpegts_fire_event(mm, ml_mux_start);
269

270
  /* Link */
271
  mpegts_mux_scan_active(mm, buf, mi);
272

273
  if (mi->mi_linked)
274
    mpegts_mux_subscribe_linked(mi, mm);
275

276
  return 0;
277
}
278

279
int
280
mpegts_mux_instance_weight ( mpegts_mux_instance_t *mmi )
281
{
282
  int w = 0;
283
  const service_t *s;
284
  const th_subscription_t *ths;
285
  mpegts_mux_t *mm = mmi->mmi_mux;
286
  lock_assert(&mmi->mmi_input->mi_output_lock);
287

288
  /* Service subs */
289
  LIST_FOREACH(s, &mm->mm_transports, s_active_link)
290
    LIST_FOREACH(ths, &s->s_subscriptions, ths_service_link)
291
      w = MAX(w, ths->ths_weight);
292

293
  return w;
294
}
295

296
/* ****************************************************************************
297
 * Class definition
298
 * ***************************************************************************/
299

300
static htsmsg_t *
301
mpegts_mux_class_save ( idnode_t *self, char *filename, size_t fsize )
302
{
303
  mpegts_mux_t *mm = (mpegts_mux_t*)self;
304
  if (mm->mm_config_save)
305
    return mm->mm_config_save(mm, filename, fsize);
306
  return NULL;
307
}
308

309
static void
310
mpegts_mux_class_delete ( idnode_t *self )
311
{
312
  mpegts_mux_t *mm = (mpegts_mux_t*)self;
313
  if (mm->mm_delete) mm->mm_delete(mm, 1);
314
}
315

316
static const char *
317
mpegts_mux_class_get_title ( idnode_t *self, const char *lang )
318
{
319
  static __thread char buf[256];
320
  mpegts_mux_nice_name((mpegts_mux_t*)self, buf, sizeof(buf));
321
  return buf;
322
}
323

324
static const void *
325
mpegts_mux_class_get_num_svc ( void *ptr )
326
{
327
  static int n;
328
  mpegts_mux_t *mm = ptr;
329
  mpegts_service_t *s;
330

331
  n = 0;
332
  LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link)
333
    n++;
334

335
  return &n;
336
}
337

338
static const void *
339
mpegts_mux_class_get_num_chn ( void *ptr )
340
{
341
  static int n;
342
  mpegts_mux_t *mm = ptr;
343
  mpegts_service_t *s;
344
  idnode_list_mapping_t *ilm;
345

346
  n = 0;
347
  LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link)
348
    LIST_FOREACH(ilm, &s->s_channels, ilm_in1_link)
349
      n++;
350

351
  return &n;
352
}
353

354
static const void *
355
mpegts_mux_class_get_network ( void *ptr )
356
{
357
  static char buf[512], *s = buf;
358
  mpegts_mux_t *mm = ptr;
359
  if (mm && mm->mm_network && mm->mm_network->mn_display_name)
360
    mm->mm_network->mn_display_name(mm->mm_network, buf, sizeof(buf));
361
  else
362
    *buf = 0;
363
  return &s;
364
}
365

366
static const void *
367
mpegts_mux_class_get_network_uuid ( void *ptr )
368
{
369
  mpegts_mux_t *mm = ptr;
370
  if (mm && mm->mm_network)
371
    idnode_uuid_as_str(&mm->mm_network->mn_id, prop_sbuf);
372
  else
373
    prop_sbuf[0] = '\0';
374
  return &prop_sbuf_ptr;
375
}
376

377
static const void *
378
mpegts_mux_class_get_name ( void *ptr )
379
{
380
  static char buf[512], *s = buf;
381
  mpegts_mux_t *mm = ptr;
382
  if (mm && mm->mm_display_name)
383
    mm->mm_display_name(mm, buf, sizeof(buf));
384
  else
385
    *buf = 0;
386
  return &s;
387
}
388

389
static struct strtab
390
scan_state_tab[] = {
391
  { N_("IDLE"),   MM_SCAN_STATE_IDLE },
392
  { N_("PEND"),   MM_SCAN_STATE_PEND },
393
  { N_("ACTIVE"), MM_SCAN_STATE_ACTIVE },
394
};
395

396
static struct strtab
397
scan_result_tab[] = {
398
 { N_("NONE"),         MM_SCAN_NONE },
399
 { N_("OK"),           MM_SCAN_OK   },
400
 { N_("FAIL"),         MM_SCAN_FAIL },
401
 { N_("OK (partial)"), MM_SCAN_PARTIAL },
402
 { N_("IGNORE"),       MM_SCAN_IGNORE },
403
};
404

405
int
406
mpegts_mux_class_scan_state_set ( void *o, const void *p )
407
{
408
  mpegts_mux_t *mm = o;
409
  int state = *(int*)p;
410

411
  /* Ignore */
412
  if (!mm->mm_is_enabled(mm))
413
    return 0;
414
  
415
  /* Start */
416
  if (state == MM_SCAN_STATE_PEND || state == MM_SCAN_STATE_ACTIVE) {
417

418
    /* No change */
419
    if (mm->mm_scan_state != MM_SCAN_STATE_IDLE)
420
      return 0;
421

422
    /* Start */
423
    mpegts_network_scan_queue_add(mm, SUBSCRIPTION_PRIO_SCAN_USER,
424
                                  SUBSCRIPTION_USERSCAN, 0);
425

426
  /* Stop */
427
  } else if (state == MM_SCAN_STATE_IDLE) {
428

429
    /* No change */
430
    if (mm->mm_scan_state == MM_SCAN_STATE_IDLE)
431
      return 0;
432

433
    /* Update */
434
    mpegts_network_scan_mux_cancel(mm, 0);
435

436
  /* Invalid */
437
  } else {
438
  }
439

440
  return 1;
441
}
442

443
static htsmsg_t *
444
mpegts_mux_class_scan_state_enum ( void *p, const char *lang )
445
{
446
  return strtab2htsmsg(scan_state_tab, 1, lang);
447
}
448

449
static htsmsg_t *
450
mpegts_mux_class_scan_result_enum ( void *p, const char *lang )
451
{
452
  return strtab2htsmsg(scan_result_tab, 1, lang);
453
}
454

455
static void
456
mpegts_mux_class_enabled_notify ( void *p, const char *lang )
457
{
458
  mpegts_mux_t *mm = p;
459
  if (!mm->mm_is_enabled(mm)) {
460
    mm->mm_stop(mm, 1, SM_CODE_MUX_NOT_ENABLED);
461
    mpegts_network_scan_mux_cancel(mm, 0);
462
    if (mm->mm_enabled == MM_IGNORE) {
463
      mpegts_mux_do_stop(mm, 1);
464
      mm->mm_scan_result = MM_SCAN_IGNORE;
465
    }
466
  }
467
}
468

469
static htsmsg_t *
470
mpegts_mux_enable_list ( void *o, const char *lang )
471
{
472
  static const struct strtab tab[] = {
473
    { N_("Ignore"),                   MM_IGNORE },
474
    { N_("Disable"),                  MM_DISABLE },
475
    { N_("Enable"),                   MM_ENABLE },
476
  };
477
  return strtab2htsmsg(tab, 1, lang);
478
}
479

480
static htsmsg_t *
481
mpegts_mux_epg_list ( void *o, const char *lang )
482
{
483
  static const struct strtab tab[] = {
484
    { N_("Disable"),                  MM_EPG_DISABLE },
485
    { N_("Enable (auto)"),            MM_EPG_ENABLE },
486
    { N_("Force (auto)"),             MM_EPG_FORCE },
487
    { N_("Only EIT"),                 MM_EPG_ONLY_EIT },
488
    { N_("Only PSIP (ATSC)"),         MM_EPG_ONLY_PSIP },
489
    { N_("Only UK Freesat"),          MM_EPG_ONLY_UK_FREESAT },
490
    { N_("Only UK Freeview"),         MM_EPG_ONLY_UK_FREEVIEW },
491
    { N_("Only Viasat Baltic"),       MM_EPG_ONLY_VIASAT_BALTIC },
492
    { N_("Only Bulsatcom 39E"),       MM_EPG_ONLY_BULSATCOM_39E },
493
    { N_("Only UK Cable"),	      MM_EPG_ONLY_UK_CABLE },
494
    { N_("Only OpenTV Sky UK"),       MM_EPG_ONLY_OPENTV_SKY_UK },
495
    { N_("Only OpenTV Sky Italia"),   MM_EPG_ONLY_OPENTV_SKY_ITALIA },
496
    { N_("Only OpenTV Sky Ausat"),    MM_EPG_ONLY_OPENTV_SKY_AUSAT },
497
  };
498
  return strtab2htsmsg(tab, 1, lang);
499
}
500

501
static htsmsg_t *
502
mpegts_mux_ac3_list ( void *o, const char *lang )
503
{
504
  static const struct strtab tab[] = {
505
    { N_("Standard"),                 MM_AC3_STANDARD },
506
    { N_("AC-3 = descriptor 6"),      MM_AC3_PMT_06 },
507
    { N_("Ignore descriptor 5"),      MM_AC3_PMT_N05 },
508
  };
509
  return strtab2htsmsg(tab, 1, lang);
510
}
511

512
CLASS_DOC(mpegts_mux)
513

514
const idclass_t mpegts_mux_class =
515
{
516
  .ic_class      = "mpegts_mux",
517
  .ic_caption    = N_("MPEG-TS Multiplex"),
518
  .ic_event      = "mpegts_mux",
519
  .ic_doc        = tvh_doc_mpegts_mux_class,
520
  .ic_perm_def   = ACCESS_ADMIN,
521
  .ic_save       = mpegts_mux_class_save,
522
  .ic_delete     = mpegts_mux_class_delete,
523
  .ic_get_title  = mpegts_mux_class_get_title,
524
  .ic_properties = (const property_t[]){
525
    {
526
      .type     = PT_INT,
527
      .id       = "enabled",
528
      .name     = N_("Enabled"),
529
      .desc     = N_("Enable, disable or ignore the mux. "
530
                     "When the mux is marked as ignore, "
531
                     "all discovered services are removed."),
532
      .off      = offsetof(mpegts_mux_t, mm_enabled),
533
      .def.i    = MM_ENABLE,
534
      .list     = mpegts_mux_enable_list,
535
      .notify   = mpegts_mux_class_enabled_notify,
536
      .opts     = PO_DOC_NLIST
537
    },
538
    {
539
      .type     = PT_INT,
540
      .id       = "epg",
541
      .name     = N_("EPG scan"),
542
      .desc     = N_("The EPG grabber to use on the mux. "
543
                     "Enable (auto) is the recommended value."),
544
      .off      = offsetof(mpegts_mux_t, mm_epg),
545
      .def.i    = MM_EPG_ENABLE,
546
      .list     = mpegts_mux_epg_list,
547
      .opts     = PO_DOC_NLIST,
548
    },
549
    {
550
      .type     = PT_STR,
551
      .id       = "network",
552
      .name     = N_("Network"),
553
      .desc     = N_("The network the mux is on."),
554
      .opts     = PO_RDONLY | PO_NOSAVE,
555
      .get      = mpegts_mux_class_get_network,
556
    },
557
    {
558
      .type     = PT_STR,
559
      .id       = "network_uuid",
560
      .name     = N_("Network UUID"),
561
      .desc     = N_("The networks' universally unique identifier (UUID)."),
562
      .opts     = PO_RDONLY | PO_NOSAVE | PO_HIDDEN | PO_EXPERT,
563
      .get      = mpegts_mux_class_get_network_uuid,
564
    },
565
    {
566
      .type     = PT_STR,
567
      .id       = "name",
568
      .name     = N_("Name"),
569
      .desc     = N_("The name (or frequency) the mux is on."),
570
      .opts     = PO_RDONLY | PO_NOSAVE,
571
      .get      = mpegts_mux_class_get_name,
572
    },
573
    {
574
      .type     = PT_STR,
575
      .id       = "pnetwork_name",
576
      .name     = N_("Provider network name"),
577
      .desc     = N_("The provider's network name."),
578
      .off      = offsetof(mpegts_mux_t, mm_provider_network_name),
579
      .opts     = PO_RDONLY | PO_HIDDEN | PO_EXPERT,
580
    },
581
    {
582
      .type     = PT_U16,
583
      .id       = "onid",
584
      .name     = N_("Original network ID"),
585
      .desc     = N_("The provider's network ID."),
586
      .opts     = PO_RDONLY | PO_ADVANCED,
587
      .off      = offsetof(mpegts_mux_t, mm_onid),
588
    },
589
    {
590
      .type     = PT_U16,
591
      .id       = "tsid",
592
      .name     = N_("Transport stream ID"),
593
      .desc     = N_("The transport stream ID of the mux within the "
594
                     "network."),
595
      .opts     = PO_RDONLY | PO_ADVANCED,
596
      .off      = offsetof(mpegts_mux_t, mm_tsid),
597
    },
598
    {
599
      .type     = PT_STR,
600
      .id       = "cridauth",
601
      .name     = N_("CRID authority"),
602
      .desc     = N_("The Content reference identifier (CRID) authority."),
603
      .opts     = PO_RDONLY | PO_HIDDEN | PO_EXPERT,
604
      .off      = offsetof(mpegts_mux_t, mm_crid_authority),
605
    },
606
    {
607
      .type     = PT_INT,
608
      .id       = "scan_state",
609
      .name     = N_("Scan status"),
610
      .desc     = N_("The scan state. New muxes will automatically be "
611
                     "changed to the PEND state. You can change this to "
612
                     "ACTIVE to queue a scan of this mux."),
613
      .off      = offsetof(mpegts_mux_t, mm_scan_state),
614
      .set      = mpegts_mux_class_scan_state_set,
615
      .list     = mpegts_mux_class_scan_state_enum,
616
      .opts     = PO_NOSAVE | PO_SORTKEY | PO_DOC_NLIST,
617
    },
618
    {
619
      .type     = PT_INT,
620
      .id       = "scan_result",
621
      .name     = N_("Scan result"),
622
      .desc     = N_("The outcome of the last scan performed on this mux."),
623
      .off      = offsetof(mpegts_mux_t, mm_scan_result),
624
      .opts     = PO_RDONLY | PO_SORTKEY | PO_DOC_NLIST,
625
      .list     = mpegts_mux_class_scan_result_enum,
626
    },
627
    {
628
      .type     = PT_STR,
629
      .id       = "charset",
630
      .name     = N_("Character set"),
631
      .desc     = N_("The character set used on this mux. You should "
632
                     "not have to change this unless channel names, etc "
633
                     " appear garbled."),
634
      .off      = offsetof(mpegts_mux_t, mm_charset),
635
      .list     = dvb_charset_enum,
636
      .opts     = PO_ADVANCED | PO_DOC_NLIST,
637
    },
638
    {
639
      .type     = PT_INT,
640
      .id       = "num_svc",
641
      .name     = N_("# Services"),
642
      .desc     = N_("The total number of services found on this mux."),
643
      .opts     = PO_RDONLY | PO_NOSAVE,
644
      .get      = mpegts_mux_class_get_num_svc,
645
    },
646
    {
647
      .type     = PT_INT,
648
      .id       = "num_chn",
649
      .name     = N_("# Channels"),
650
      .desc     = N_("The number of services on the mux that are "
651
                     "mapped to channels."),
652
      .opts     = PO_RDONLY | PO_NOSAVE,
653
      .get      = mpegts_mux_class_get_num_chn,
654
    },
655
    {
656
       .type     = PT_BOOL,
657
       .id       = "tsid_zero",
658
       .name     = N_("Accept zero value for TSID"),
659
       .off      = offsetof(mpegts_mux_t, mm_tsid_accept_zero_value),
660
       .opts     = PO_ADVANCED
661
    },
662
    {
663
      .type     = PT_INT,
664
      .id       = "pmt_06_ac3",
665
      .name     = N_("AC-3 detection"),
666
      .desc     = N_("Use AC-3 detection on the mux."),
667
      .off      = offsetof(mpegts_mux_t, mm_pmt_ac3),
668
      .def.i    = MM_AC3_STANDARD,
669
      .list     = mpegts_mux_ac3_list,
670
      .opts     = PO_HIDDEN | PO_EXPERT | PO_DOC_NLIST,
671
    },
672
    {
673
      .type     = PT_BOOL,
674
      .id       = "eit_tsid_nocheck",
675
      .name     = N_("EIT - skip TSID check"),
676
      .desc     = N_("Skip TSID checking. For when providers use invalid "
677
                     "Transport Stream IDs."),
678
      .off      = offsetof(mpegts_mux_t, mm_eit_tsid_nocheck),
679
      .opts     = PO_HIDDEN | PO_EXPERT
680
    },
681
    {}
682
  }
683
};
684

685
/* ****************************************************************************
686
 * Class methods
687
 * ***************************************************************************/
688

689
static void
690
mpegts_mux_display_name ( mpegts_mux_t *mm, char *buf, size_t len )
691
{
692
  snprintf(buf, len, "Multiplex [onid:%04X tsid:%04X]",
693
           mm->mm_onid, mm->mm_tsid);
694
}
695

696
static void
697
mpegts_mux_do_stop ( mpegts_mux_t *mm, int delconf )
698
{
699
  mpegts_mux_instance_t *mmi;
700
  th_subscription_t *ths;
701
  mpegts_service_t *s;
702

703
  /* Cancel scan */
704
  mpegts_network_scan_queue_del(mm);
705

706
  /* Remove instances */
707
  while ((mmi = LIST_FIRST(&mm->mm_instances))) {
708
    mmi->tii_delete((tvh_input_instance_t *)mmi);
709
  }
710

711
  /* Remove raw subscribers */
712
  while ((ths = LIST_FIRST(&mm->mm_raw_subs))) {
713
    subscription_unsubscribe(ths, 0);
714
  }
715

716
  /* Delete services */
717
  while ((s = LIST_FIRST(&mm->mm_services))) {
718
    service_destroy((service_t*)s, delconf);
719
  }
720

721
  /* Stop PID timer */
722
  mtimer_disarm(&mm->mm_update_pids_timer);
723
}
724

725
void
726
mpegts_mux_free ( mpegts_mux_t *mm )
727
{
728
  free(mm->mm_provider_network_name);
729
  free(mm->mm_crid_authority);
730
  free(mm->mm_charset);
731
  free(mm);
732
}
733

734
void
735
mpegts_mux_delete ( mpegts_mux_t *mm, int delconf )
736
{
737
  char buf[256];
738

739
  idnode_save_check(&mm->mm_id, delconf);
740

741
  mpegts_mux_nice_name(mm, buf, sizeof(buf));
742
  tvhinfo(LS_MPEGTS, "%s (%p) - deleting", buf, mm);
743

744
  /* Stop */
745
  mm->mm_stop(mm, 1, SM_CODE_ABORTED);
746

747
  /* Remove from network */
748
  LIST_REMOVE(mm, mm_network_link);
749

750
  /* Real stop */
751
  mpegts_mux_do_stop(mm, delconf);
752

753
  /* Free memory */
754
  idnode_save_check(&mm->mm_id, 1);
755
  idnode_unlink(&mm->mm_id);
756
  mpegts_mux_release(mm);
757
}
758

759
static htsmsg_t *
760
mpegts_mux_config_save ( mpegts_mux_t *mm, char *filename, size_t fsize )
761
{
762
  return NULL;
763
}
764

765
static int
766
mpegts_mux_is_enabled ( mpegts_mux_t *mm )
767
{
768
  return mm->mm_enabled == MM_ENABLE;
769
}
770

771
static int
772
mpegts_mux_is_epg ( mpegts_mux_t *mm )
773
{
774
  return mm->mm_epg;
775
}
776

 
777
static void
778
mpegts_mux_create_instances ( mpegts_mux_t *mm )
779
{
780
}
781

782
static int
783
mpegts_mux_has_subscribers ( mpegts_mux_t *mm, const char *name )
784
{
785
  mpegts_mux_instance_t *mmi = mm->mm_active;
786
  if (mmi) {
787
    if (mmi->mmi_input->mi_has_subscription(mmi->mmi_input, mm)) {
788
      tvhtrace(LS_MPEGTS, "%s - keeping mux", name);
789
      return 1;
790
    }
791
  }
792
  return 0;
793
}
794

795
static void
796
mpegts_mux_stop ( mpegts_mux_t *mm, int force, int reason )
797
{
798
  char buf[256], buf2[256], *s;
799
  mpegts_mux_instance_t *mmi = mm->mm_active, *mmi2;
800
  mpegts_input_t *mi = NULL, *mi2;
801
  mpegts_pid_t *mp;
802
  mpegts_pid_sub_t *mps;
803

804
  mpegts_mux_nice_name(mm, buf, sizeof(buf));
805

806
  if (!force && mpegts_mux_has_subscribers(mm, buf))
807
    return;
808

809
  /* Stop possible recursion */
810
  if (!mmi) return;
811

812
  tvhdebug(LS_MPEGTS, "%s - stopping mux%s", buf, force ? " (forced)" : "");
813

814
  mi = mmi->mmi_input;
815
  assert(mi);
816

817
  /* Linked input */
818
  if (mi->mi_linked) {
819
    mi2 = mpegts_input_find(mi->mi_linked);
820
    if (mi2 && (mmi2 = LIST_FIRST(&mi2->mi_mux_active)) != NULL) {
821
      mpegts_mux_nice_name(mmi2->mmi_mux, buf2, sizeof(buf2));
822
      if (mmi2 && !mpegts_mux_has_subscribers(mmi2->mmi_mux, buf2)) {
823
        s = mi2->mi_linked;
824
        mi2->mi_linked = NULL;
825
        mpegts_mux_unsubscribe_linked(mi2, NULL);
826
        mi2->mi_linked = s;
827
      } else {
828
        if (!force) {
829
          tvhtrace(LS_MPEGTS, "%s - keeping subscribed (linked tuner active)", buf);
830
          return;
831
        }
832
      }
833
    }
834
  }
835

836
  if (mm->mm_active != mmi)
837
    return;
838

839
  mi->mi_stopping_mux(mi, mmi);
840
  mi->mi_stop_mux(mi, mmi);
841
  mi->mi_stopped_mux(mi, mmi);
842

843
  assert(mm->mm_active == NULL);
844

845
  /* Flush all tables */
846
  tvhtrace(LS_MPEGTS, "%s - flush tables", buf);
847
  mpegts_table_flush_all(mm);
848

849
  tvhtrace(LS_MPEGTS, "%s - mi=%p", buf, (void *)mi);
850
  /* Flush table data queue */
851
  mpegts_input_flush_mux(mi, mm);
852

853
  /* Ensure PIDs are cleared */
854
  pthread_mutex_lock(&mi->mi_output_lock);
855
  mm->mm_last_pid = -1;
856
  mm->mm_last_mp = NULL;
857
  while ((mp = RB_FIRST(&mm->mm_pids))) {
858
    assert(mi);
859
    if (mp->mp_pid == MPEGTS_FULLMUX_PID ||
860
        mp->mp_pid == MPEGTS_TABLES_PID) {
861
      while ((mps = LIST_FIRST(&mm->mm_all_subs))) {
862
        tvhdebug(LS_MPEGTS, "%s - close PID %s subscription [%d/%p]",
863
                 buf, mp->mp_pid == MPEGTS_TABLES_PID ? "tables" : "fullmux",
864
                 mps->mps_type, mps->mps_owner);
865
        LIST_REMOVE(mps, mps_svcraw_link);
866
        free(mps);
867
      }
868
    } else {
869
      while ((mps = RB_FIRST(&mp->mp_subs))) {
870
        tvhdebug(LS_MPEGTS, "%s - close PID %04X (%d) [%d/%p]", buf,
871
                 mp->mp_pid, mp->mp_pid, mps->mps_type, mps->mps_owner);
872
        RB_REMOVE(&mp->mp_subs, mps, mps_link);
873
        if (mps->mps_type & (MPS_SERVICE|MPS_ALL))
874
          LIST_REMOVE(mps, mps_svcraw_link);
875
        if (mps->mps_type & MPS_RAW)
876
          LIST_REMOVE(mps, mps_raw_link);
877
        free(mps);
878
      }
879
    }
880
    RB_REMOVE(&mm->mm_pids, mp, mp_link);
881
    free(mp);
882
  }
883
  pthread_mutex_unlock(&mi->mi_output_lock);
884

885
  /* Scanning */
886
  mpegts_network_scan_mux_cancel(mm, 1);
887

888
  /* Events */
889
  mpegts_fire_event1(mm, ml_mux_stop, reason);
890

891
  free(mm->mm_fastscan_muxes);
892
  mm->mm_fastscan_muxes = NULL;
893
}
894

895
static void
896
mpegts_mux_update_pids_cb ( void *aux )
897
{
898
  mpegts_mux_t *mm = aux;
899
  mpegts_input_t *mi;
900

901
  if (mm && mm->mm_active) {
902
    mi = mm->mm_active->mmi_input;
903
    if (mi) {
904
      pthread_mutex_lock(&mi->mi_output_lock);
905
      mm->mm_update_pids_flag = 0;
906
      mi->mi_update_pids(mi, mm);
907
      pthread_mutex_unlock(&mi->mi_output_lock);
908
    }
909
  }
910
}
911

912
void
913
mpegts_mux_update_pids ( mpegts_mux_t *mm )
914
{
915
  if (mm && mm->mm_active)
916
    mtimer_arm_rel(&mm->mm_update_pids_timer, mpegts_mux_update_pids_cb, mm, 0);
917
}
918

919
void
920
mpegts_mux_open_table ( mpegts_mux_t *mm, mpegts_table_t *mt, int subscribe )
921
{
922
  mpegts_input_t *mi;
923

924
  lock_assert(&mm->mm_tables_lock);
925

926
  if (mt->mt_destroyed)
927
    return;
928
  if (!mm->mm_active || !mm->mm_active->mmi_input) {
929
    mt->mt_subscribed = 0;
930
    LIST_INSERT_HEAD(&mm->mm_tables, mt, mt_link);
931
    mm->mm_num_tables++;
932
    return;
933
  }
934
  if (mt->mt_flags & MT_DEFER) {
935
    if (mt->mt_defer_cmd == MT_DEFER_OPEN_PID)
936
      return;
937
    mpegts_table_grab(mt); /* thread will release the table */
938
    LIST_INSERT_HEAD(&mm->mm_tables, mt, mt_link);
939
    mm->mm_num_tables++;
940
    mt->mt_defer_cmd = MT_DEFER_OPEN_PID;
941
    TAILQ_INSERT_TAIL(&mm->mm_defer_tables, mt, mt_defer_link);
942
    return;
943
  }
944
  mi = mm->mm_active->mmi_input;
945
  LIST_INSERT_HEAD(&mm->mm_tables, mt, mt_link);
946
  mm->mm_num_tables++;
947
  if (subscribe && !mt->mt_subscribed) {
948
    mpegts_table_grab(mt);
949
    mt->mt_subscribed = 1;
950
    pthread_mutex_unlock(&mm->mm_tables_lock);
951
    pthread_mutex_lock(&mi->mi_output_lock);
952
    mpegts_input_open_pid(mi, mm, mt->mt_pid, mpegts_table_type(mt), mt->mt_weight, mt, 0);
953
    pthread_mutex_unlock(&mi->mi_output_lock);
954
    pthread_mutex_lock(&mm->mm_tables_lock);
955
    mpegts_table_release(mt);
956
  }
957
}
958

959
void
960
mpegts_mux_unsubscribe_table ( mpegts_mux_t *mm, mpegts_table_t *mt )
961
{
962
  mpegts_input_t *mi;
963

964
  lock_assert(&mm->mm_tables_lock);
965

966
  mi = mm->mm_active->mmi_input;
967
  if (mt->mt_subscribed) {
968
    mpegts_table_grab(mt);
969
    mt->mt_subscribed = 0;
970
    pthread_mutex_unlock(&mm->mm_tables_lock);
971
    pthread_mutex_lock(&mi->mi_output_lock);
972
    mpegts_input_close_pid(mi, mm, mt->mt_pid, mpegts_table_type(mt), mt->mt_weight, mt);
973
    pthread_mutex_unlock(&mi->mi_output_lock);
974
    pthread_mutex_lock(&mm->mm_tables_lock);
975
    mpegts_table_release(mt);
976
  }
977
  if ((mt->mt_flags & MT_DEFER) && mt->mt_defer_cmd == MT_DEFER_OPEN_PID) {
978
    TAILQ_REMOVE(&mm->mm_defer_tables, mt, mt_defer_link);
979
    mt->mt_defer_cmd = 0;
980
    mpegts_table_release(mt);
981
  }
982
}
983

984
void
985
mpegts_mux_close_table ( mpegts_mux_t *mm, mpegts_table_t *mt )
986
{
987
  lock_assert(&mm->mm_tables_lock);
988

989
  if (!mm->mm_active || !mm->mm_active->mmi_input) {
990
    if (mt->mt_defer_cmd) {
991
      TAILQ_REMOVE(&mm->mm_defer_tables, mt, mt_defer_link);
992
      mt->mt_defer_cmd = 0;
993
      if (mpegts_table_release(mt))
994
        return;
995
    }
996
    mt->mt_subscribed = 0;
997
    LIST_REMOVE(mt, mt_link);
998
    mm->mm_num_tables--;
999
    return;
1000
  }
1001
  if (mt->mt_flags & MT_DEFER) {
1002
    if (mt->mt_defer_cmd == MT_DEFER_CLOSE_PID)
1003
      return;
1004
    LIST_REMOVE(mt, mt_link);
1005
    mm->mm_num_tables--;
1006
    if (mt->mt_defer_cmd == MT_DEFER_OPEN_PID) {
1007
      TAILQ_REMOVE(&mm->mm_defer_tables, mt, mt_defer_link);
1008
      mt->mt_defer_cmd = 0;
1009
      mpegts_table_release(mt);
1010
      return;
1011
    }
1012
    mpegts_table_grab(mt); /* thread will free the table */
1013
    mt->mt_defer_cmd = MT_DEFER_CLOSE_PID;
1014
    TAILQ_INSERT_TAIL(&mm->mm_defer_tables, mt, mt_defer_link);
1015
    return;
1016
  }
1017
  LIST_REMOVE(mt, mt_link);
1018
  mm->mm_num_tables--;
1019
  mm->mm_unsubscribe_table(mm, mt);
1020
}
1021

1022
/* **************************************************************************
1023
 * Scanning
1024
 * *************************************************************************/
1025

1026
static void
1027
mpegts_mux_scan_service_check ( mpegts_mux_t *mm )
1028
{
1029
  mpegts_service_t *s, *snext;
1030
  time_t last_seen;
1031

1032
  /*
1033
   * Disable "not seen" services. It's quite easy algorithm which
1034
   * compares the time for the most recent services with others.
1035
   * If there is a big gap (24 hours) the service will be removed.
1036
   *
1037
   * Note that this code is run only when the PAT table scan is
1038
   * fully completed (all live services are known at this point).
1039
   */
1040
  last_seen = 0;
1041
  LIST_FOREACH(s, &mm->mm_services, s_dvb_mux_link)
1042
    if (last_seen < s->s_dvb_check_seen)
1043
      last_seen = s->s_dvb_check_seen;
1044
  for (s = LIST_FIRST(&mm->mm_services); s; s = snext) {
1045
    snext = LIST_NEXT(s, s_dvb_mux_link);
1046
    if (s->s_enabled && s->s_auto != SERVICE_AUTO_OFF &&
1047
        s->s_dvb_check_seen + 24 * 3600 < last_seen) {
1048
      tvhinfo(LS_MPEGTS, "disabling service %s [sid %04X/%d] (missing in PAT/SDT)",
1049
              s->s_nicename ?: "<unknown>", s->s_dvb_service_id, s->s_dvb_service_id);
1050
      service_set_enabled((service_t *)s, 0, SERVICE_AUTO_PAT_MISSING);
1051
    }
1052
  }
1053
}
1054

1055
void
1056
mpegts_mux_scan_done ( mpegts_mux_t *mm, const char *buf, int res )
1057
{
1058
  mpegts_table_t *mt;
1059
  int total = 0, incomplete = 0;
1060

1061
  assert(mm->mm_scan_state == MM_SCAN_STATE_ACTIVE);
1062

1063
  /* Log */
1064
  pthread_mutex_lock(&mm->mm_tables_lock);
1065
  mpegts_table_consistency_check(mm);
1066
  LIST_FOREACH(mt, &mm->mm_tables, mt_link) {
1067
    if (mt->mt_flags & MT_QUICKREQ) {
1068
      const char *s = "not found";
1069
      if (mt->mt_complete) {
1070
        s = "complete";
1071
        total++;
1072
      } else if (mt->mt_count) {
1073
        s = "incomplete";
1074
        total++;
1075
        incomplete++;
1076
      }
1077
      tvhdebug(LS_MPEGTS, "%s - %04X (%d) %s %s", buf, mt->mt_pid, mt->mt_pid, mt->mt_name, s);
1078
    }
1079
  }
1080
  pthread_mutex_unlock(&mm->mm_tables_lock);
1081

1082
  /* override if all tables were found */
1083
  if (res < 0 && incomplete <= 0 && total > 2)
1084
    res = 1;
1085

1086
  if (res < 0) {
1087
    /* is threshold 3 missing tables enough? */
1088
    if (incomplete > 0 && total > incomplete && incomplete <= 3) {
1089
      tvhinfo(LS_MPEGTS, "%s - scan complete (partial - %d/%d tables)", buf, total, incomplete);
1090
      mpegts_network_scan_mux_partial(mm);
1091
    } else {
1092
      tvhwarn(LS_MPEGTS, "%s - scan timed out (%d/%d tables)", buf, total, incomplete);
1093
      mpegts_network_scan_mux_fail(mm);
1094
    }
1095
  } else if (res) {
1096
    tvhinfo(LS_MPEGTS, "%s scan complete", buf);
1097
    mpegts_network_scan_mux_done(mm);
1098
    mpegts_mux_scan_service_check(mm);
1099
  } else {
1100
    tvhinfo(LS_MPEGTS, "%s - scan no data, failed", buf);
1101
    mpegts_network_scan_mux_fail(mm);
1102
  }
1103
}
1104

1105
static void
1106
mpegts_mux_scan_timeout ( void *aux )
1107
{
1108
  int c, q, w;
1109
  char buf[256];
1110
  mpegts_mux_t *mm = aux;
1111
  mpegts_table_t *mt;
1112
  mpegts_mux_nice_name(mm, buf, sizeof(buf));
1113

1114
  /* Timeout */
1115
  if (mm->mm_scan_init) {
1116
    mpegts_mux_scan_done(mm, buf, -1);
1117
    return;
1118
  }
1119
  mm->mm_scan_init = 1;
1120
  
1121
  /* Check tables */
1122
again:
1123
  pthread_mutex_lock(&mm->mm_tables_lock);
1124
  mpegts_table_consistency_check(mm);
1125
  c = q = w = 0;
1126
  LIST_FOREACH(mt, &mm->mm_tables, mt_link) {
1127
    if (!(mt->mt_flags & MT_QUICKREQ) && !mt->mt_working) continue;
1128
    if (!mt->mt_count) {
1129
      mpegts_table_grab(mt);
1130
      pthread_mutex_unlock(&mm->mm_tables_lock);
1131
      mpegts_table_destroy(mt);
1132
      mpegts_table_release(mt);
1133
      goto again;
1134
    } else if (!mt->mt_complete || mt->mt_working) {
1135
      q++;
1136
      if (mt->mt_working)
1137
        w++;
1138
    } else {
1139
      c++;
1140
    }
1141
  }
1142
  pthread_mutex_unlock(&mm->mm_tables_lock);
1143
      
1144
  /* No DATA - give up now */
1145
  if (!c) {
1146
    mpegts_mux_scan_done(mm, buf, 0);
1147

1148
  /* Pending tables (another 20s or 30s - bit arbitrary) */
1149
  } else if (q) {
1150
    tvhtrace(LS_MPEGTS, "%s - scan needs more time", buf);
1151
    mtimer_arm_rel(&mm->mm_scan_timeout, mpegts_mux_scan_timeout, mm, sec2mono(w ? 30 : 20));
1152
    return;
1153

1154
  /* Complete */
1155
  } else {
1156
    mpegts_mux_scan_done(mm, buf, 1);
1157
  }
1158
}
1159

1160
/* **************************************************************************
1161
 * Creation / Config
1162
 * *************************************************************************/
1163

1164
mpegts_mux_t *
1165
mpegts_mux_create0
1166
  ( mpegts_mux_t *mm, const idclass_t *class, const char *uuid,
1167
    mpegts_network_t *mn, uint16_t onid, uint16_t tsid, htsmsg_t *conf )
1168
{
1169
  char buf[256];
1170

1171
  if (idnode_insert(&mm->mm_id, uuid, class, 0)) {
1172
    if (uuid)
1173
      tvherror(LS_MPEGTS, "invalid mux uuid '%s'", uuid);
1174
    free(mm);
1175
    return NULL;
1176
  }
1177

1178
  mm->mm_refcount            = 1;
1179

1180
  /* Enabled by default */
1181
  mm->mm_enabled             = MM_ENABLE;
1182
  mm->mm_epg                 = MM_EPG_ENABLE;
1183

1184
  /* Identification */
1185
  mm->mm_onid                = onid;
1186
  mm->mm_tsid                = tsid;
1187

1188
  /* Add to network */
1189
  LIST_INSERT_HEAD(&mn->mn_muxes, mm, mm_network_link);
1190
  mm->mm_network             = mn;
1191

1192
  /* Debug/Config */
1193
  mm->mm_delete              = mpegts_mux_delete;
1194
  mm->mm_free                = mpegts_mux_free;
1195
  mm->mm_display_name        = mpegts_mux_display_name;
1196
  mm->mm_config_save         = mpegts_mux_config_save;
1197
  mm->mm_is_enabled          = mpegts_mux_is_enabled;
1198
  mm->mm_is_epg              = mpegts_mux_is_epg;
1199

1200
  /* Start/stop */
1201
  mm->mm_stop                = mpegts_mux_stop;
1202
  mm->mm_create_instances    = mpegts_mux_create_instances;
1203

1204
  /* Table processing */
1205
  mm->mm_open_table          = mpegts_mux_open_table;
1206
  mm->mm_unsubscribe_table   = mpegts_mux_unsubscribe_table;
1207
  mm->mm_close_table         = mpegts_mux_close_table;
1208
  pthread_mutex_init(&mm->mm_tables_lock, NULL);
1209
  TAILQ_INIT(&mm->mm_table_queue);
1210
  TAILQ_INIT(&mm->mm_defer_tables);
1211
  LIST_INIT(&mm->mm_descrambler_caids);
1212
  TAILQ_INIT(&mm->mm_descrambler_tables);
1213
  TAILQ_INIT(&mm->mm_descrambler_emms);
1214
  pthread_mutex_init(&mm->mm_descrambler_lock, NULL);
1215

1216
  mm->mm_last_pid            = -1;
1217

1218
  /* Configuration */
1219
  if (conf)
1220
    idnode_load(&mm->mm_id, conf);
1221

1222
  if (mm->mm_enabled == MM_IGNORE)
1223
    mm->mm_scan_result = MM_SCAN_IGNORE;
1224

1225
  /* Initial scan */
1226
  if (mm->mm_scan_result == MM_SCAN_NONE || !mn->mn_skipinitscan)
1227
    mpegts_network_scan_queue_add(mm, SUBSCRIPTION_PRIO_SCAN_INIT,
1228
                                  SUBSCRIPTION_INITSCAN, 10);
1229
  else if (mm->mm_network->mn_idlescan)
1230
    mpegts_network_scan_queue_add(mm, SUBSCRIPTION_PRIO_SCAN_IDLE,
1231
                                  SUBSCRIPTION_IDLESCAN, 10);
1232

1233
  mpegts_mux_nice_name(mm, buf, sizeof(buf));
1234
  tvhtrace(LS_MPEGTS, "%s - created", buf);
1235

1236
  return mm;
1237
}
1238

1239
void
1240
mpegts_mux_save ( mpegts_mux_t *mm, htsmsg_t *c )
1241
{
1242
  mpegts_service_t *ms;
1243
  htsmsg_t *root = htsmsg_create_map();
1244
  htsmsg_t *services = htsmsg_create_map();
1245
  htsmsg_t *e;
1246
  char ubuf[UUID_HEX_SIZE];
1247

1248
  idnode_save(&mm->mm_id, root);
1249
  LIST_FOREACH(ms, &mm->mm_services, s_dvb_mux_link) {
1250
    e = htsmsg_create_map();
1251
    service_save((service_t *)ms, e);
1252
    htsmsg_add_msg(services, idnode_uuid_as_str(&ms->s_id, ubuf), e);
1253
  }
1254
  htsmsg_add_msg(root, "services", services);
1255
  htsmsg_add_msg(c, "config", root);
1256
}
1257

1258
int
1259
mpegts_mux_set_network_name ( mpegts_mux_t *mm, const char *name )
1260
{
1261
  if (strcmp(mm->mm_provider_network_name ?: "", name ?: "")) {
1262
    tvh_str_update(&mm->mm_provider_network_name, name ?: "");
1263
    return 1;
1264
  }
1265
  return 0;
1266
}
1267

1268
int
1269
mpegts_mux_set_onid ( mpegts_mux_t *mm, uint16_t onid )
1270
{
1271
  if (onid == mm->mm_onid)
1272
    return 0;
1273
  mm->mm_onid = onid;
1274
  if (tvhtrace_enabled()) {
1275
    char buf[256];
1276
    mpegts_mux_nice_name(mm, buf, sizeof(buf));
1277
    tvhtrace(LS_MPEGTS, "%s - set onid %04X (%d)", buf, onid, onid);
1278
  }
1279
  idnode_changed(&mm->mm_id);
1280
  return 1;
1281
}
1282

1283
int
1284
mpegts_mux_set_tsid ( mpegts_mux_t *mm, uint16_t tsid, int force )
1285
{
1286
  if (tsid == mm->mm_tsid)
1287
    return 0;
1288
  if (!force && mm->mm_tsid != MPEGTS_TSID_NONE)
1289
    return 0;
1290
  mm->mm_tsid = tsid;
1291
  if (tvhtrace_enabled()) {
1292
    char buf[256];
1293
    mpegts_mux_nice_name(mm, buf, sizeof(buf));
1294
    tvhtrace(LS_MPEGTS, "%s - set tsid %04X (%d)", buf, tsid, tsid);
1295
  }
1296
  idnode_changed(&mm->mm_id);
1297
  return 1;
1298
}
1299

1300
int 
1301
mpegts_mux_set_crid_authority ( mpegts_mux_t *mm, const char *defauth )
1302
{
1303
  if (defauth && !strcmp(defauth, mm->mm_crid_authority ?: ""))
1304
    return 0;
1305
  tvh_str_update(&mm->mm_crid_authority, defauth);
1306
  if (tvhtrace_enabled()) {
1307
    char buf[256];
1308
    mpegts_mux_nice_name(mm, buf, sizeof(buf));
1309
    tvhtrace(LS_MPEGTS, "%s - set crid authority %s", buf, defauth);
1310
  }
1311
  idnode_changed(&mm->mm_id);
1312
  return 1;
1313
}
1314

1315
void
1316
mpegts_mux_nice_name( mpegts_mux_t *mm, char *buf, size_t len )
1317
{
1318
  size_t len2;
1319

1320
  if (len == 0 || buf == NULL || mm == NULL) {
1321
    if (buf && len > 0)
1322
      *buf = '\0';
1323
    return;
1324
  }
1325
  if (mm->mm_display_name)
1326
    mm->mm_display_name(mm, buf, len);
1327
  else
1328
    *buf = '\0';
1329
  len2 = strlen(buf);
1330
  buf += len2;
1331
  len -= len2;
1332
  if (len2 + 16 >= len)
1333
    return;
1334
  strcpy(buf, " in ");
1335
  buf += 4;
1336
  len -= 4;
1337
  if (mm->mm_network && mm->mm_network->mn_display_name)
1338
    mm->mm_network->mn_display_name(mm->mm_network, buf, len);
1339
}
1340

1341
/* **************************************************************************
1342
 * Subscriptions
1343
 * *************************************************************************/
1344

1345
void
1346
mpegts_mux_remove_subscriber
1347
  ( mpegts_mux_t *mm, th_subscription_t *s, int reason )
1348
{
1349
  if (tvhtrace_enabled()) {
1350
    char buf[256];
1351
    mpegts_mux_nice_name(mm, buf, sizeof(buf));
1352
    tvhtrace(LS_MPEGTS, "%s - remove subscriber (reason %i)", buf, reason);
1353
  }
1354
  mm->mm_stop(mm, 0, reason);
1355
}
1356

1357
int
1358
mpegts_mux_subscribe
1359
  ( mpegts_mux_t *mm, mpegts_input_t *mi,
1360
    const char *name, int weight, int flags )
1361
{
1362
  profile_chain_t prch;
1363
  th_subscription_t *s;
1364
  int err = 0;
1365
  memset(&prch, 0, sizeof(prch));
1366
  prch.prch_id = mm;
1367
  s = subscription_create_from_mux(&prch, (tvh_input_t *)mi,
1368
                                   weight, name,
1369
                                   SUBSCRIPTION_NONE | flags,
1370
                                   NULL, NULL, NULL, &err);
1371
  return s ? 0 : (err ? err : SM_CODE_UNDEFINED_ERROR);
1372
}
1373

1374
void
1375
mpegts_mux_unsubscribe_by_name
1376
  ( mpegts_mux_t *mm, const char *name )
1377
{
1378
  const service_t *t;
1379
  th_subscription_t *s, *n;
1380

1381
  s = LIST_FIRST(&mm->mm_raw_subs);
1382
  while (s) {
1383
    n = LIST_NEXT(s, ths_mux_link);
1384
    t = s->ths_service;
1385
    if (t && t->s_type == STYPE_RAW && !strcmp(s->ths_title, name))
1386
      subscription_unsubscribe(s, UNSUBSCRIBE_FINAL);
1387
    s = n;
1388
  }
1389
}
1390

1391
th_subscription_t *
1392
mpegts_mux_find_subscription_by_name
1393
  ( mpegts_mux_t *mm, const char *name )
1394
{
1395
  const service_t *t;
1396
  th_subscription_t *s;
1397

1398
  LIST_FOREACH(s, &mm->mm_raw_subs, ths_mux_link) {
1399
    t = s->ths_service;
1400
    if (t && t->s_type == STYPE_RAW && !strcmp(s->ths_title, name))
1401
      return s;
1402
  }
1403
  return NULL;
1404
}
1405

1406
void
1407
mpegts_mux_tuning_error ( const char *mux_uuid, mpegts_mux_instance_t *mmi_match )
1408
{
1409
  mpegts_mux_t *mm;
1410
  mpegts_mux_instance_t *mmi;
1411

1412
  if (!tvh_mutex_timedlock(&global_lock, 2000000)) {
1413
    mm = mpegts_mux_find(mux_uuid);
1414
    if (mm) {
1415
      if ((mmi = mm->mm_active) != NULL && mmi == mmi_match)
1416
        if (mmi->mmi_input)
1417
          mmi->mmi_input->mi_tuning_error(mmi->mmi_input, mm);
1418
    }
1419
    pthread_mutex_unlock(&global_lock);
1420
  }
1421
}
1422

1423
/* **************************************************************************
1424
 * Search
1425
 * *************************************************************************/
1426

1427
mpegts_service_t *
1428
mpegts_mux_find_service ( mpegts_mux_t *mm, uint16_t sid )
1429
{
1430
  mpegts_service_t *ms;
1431
  LIST_FOREACH(ms, &mm->mm_services, s_dvb_mux_link)
1432
    if (ms->s_dvb_service_id == sid && ms->s_enabled)
1433
      break;
1434
  return ms;
1435
}
1436

1437
static int mp_cmp ( mpegts_pid_t *a, mpegts_pid_t *b )
1438
{
1439
  return a->mp_pid - b->mp_pid;
1440
}
1441

1442
mpegts_pid_t *
1443
mpegts_mux_find_pid_ ( mpegts_mux_t *mm, int pid, int create )
1444
{
1445
  mpegts_pid_t skel, *mp;
1446

1447
  if (pid < 0 || pid > MPEGTS_TABLES_PID) return NULL;
1448

1449
  skel.mp_pid = pid;
1450
  mp = RB_FIND(&mm->mm_pids, &skel, mp_link, mp_cmp);
1451
  if (mp == NULL) {
1452
    if (create) {
1453
      mp = calloc(1, sizeof(*mp));
1454
      mp->mp_pid = pid;
1455
      if (!RB_INSERT_SORTED(&mm->mm_pids, mp, mp_link, mp_cmp)) {
1456
        mp->mp_cc = -1;
1457
      } else {
1458
        free(mp);
1459
        mp = NULL;
1460
      }
1461
    }
1462
  }
1463
  if (mp) {
1464
    mm->mm_last_pid = pid;
1465
    mm->mm_last_mp = mp;
1466
  }
1467
  return mp;
1468
}
1469

1470
/* **************************************************************************
1471
 * Misc
1472
 * *************************************************************************/
1473

1474
int
1475
mpegts_mux_compare ( mpegts_mux_t *a, mpegts_mux_t *b )
1476
{
1477
  int r = uuid_cmp(&a->mm_network->mn_id.in_uuid,
1478
                   &b->mm_network->mn_id.in_uuid);
1479
  if (r)
1480
    return r;
1481
  if (idnode_is_instance(&a->mm_id, &dvb_mux_dvbs_class) &&
1482
      idnode_is_instance(&b->mm_id, &dvb_mux_dvbs_class)) {
1483
    dvb_mux_conf_t *mc1 = &((dvb_mux_t *)a)->lm_tuning;
1484
    dvb_mux_conf_t *mc2 = &((dvb_mux_t *)b)->lm_tuning;
1485
    assert(mc1->dmc_fe_type == DVB_TYPE_S);
1486
    assert(mc2->dmc_fe_type == DVB_TYPE_S);
1487
    r = (int)mc1->u.dmc_fe_qpsk.polarisation -
1488
        (int)mc2->u.dmc_fe_qpsk.polarisation;
1489
    if (r == 0)
1490
      r = mc1->dmc_fe_freq - mc2->dmc_fe_freq;
1491
  }
1492
  return r;
1493
}
1494

1495
/******************************************************************************
1496
 * Editor Configuration
1497
 *
1498
 * vim:sts=2:ts=2:sw=2:et
1499
 *****************************************************************************/
(3-3/4)