576 lines
14 KiB
C
576 lines
14 KiB
C
/*
|
|
* Tvheadend
|
|
* Copyright (C) 2010 Andreas Öman
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef SERVICE_H__
|
|
#define SERVICE_H__
|
|
|
|
#include "htsmsg.h"
|
|
#include "idnode.h"
|
|
#include "descrambler.h"
|
|
|
|
extern const idclass_t service_class;
|
|
|
|
extern struct service_queue service_all;
|
|
|
|
struct channel;
|
|
|
|
/**
|
|
* Stream, one media component for a service.
|
|
*/
|
|
typedef struct elementary_stream {
|
|
|
|
TAILQ_ENTRY(elementary_stream) es_link;
|
|
TAILQ_ENTRY(elementary_stream) es_filt_link;
|
|
int es_position;
|
|
struct service *es_service;
|
|
|
|
streaming_component_type_t es_type;
|
|
int es_index;
|
|
|
|
uint16_t es_aspect_num;
|
|
uint16_t es_aspect_den;
|
|
|
|
char es_lang[4]; /* ISO 639 2B 3-letter language code */
|
|
uint8_t es_audio_type; /* Audio type */
|
|
|
|
uint16_t es_composition_id;
|
|
uint16_t es_ancillary_id;
|
|
|
|
int16_t es_pid;
|
|
uint16_t es_parent_pid; /* For subtitle streams originating from
|
|
a teletext stream. this is the pid
|
|
of the teletext stream */
|
|
int8_t es_pid_opened; /* PID is opened */
|
|
|
|
int8_t es_cc; /* Last CC */
|
|
|
|
avgstat_t es_cc_errors;
|
|
avgstat_t es_rate;
|
|
|
|
int es_peak_presentation_delay; /* Max seen diff. of DTS and PTS */
|
|
|
|
/* PCR recovery */
|
|
|
|
int es_pcr_recovery_fails;
|
|
int64_t es_pcr_real_last; /* realtime clock when we saw last PCR */
|
|
int64_t es_pcr_last; /* PCR clock when we saw last PCR */
|
|
int64_t es_pcr_drift;
|
|
|
|
/* For service stream packet reassembly */
|
|
|
|
sbuf_t es_buf;
|
|
|
|
uint32_t es_startcond;
|
|
uint32_t es_startcode;
|
|
uint32_t es_startcode_offset;
|
|
int es_parser_state;
|
|
int es_parser_ptr;
|
|
void *es_priv; /* Parser private data */
|
|
|
|
sbuf_t es_buf_ps; // program stream reassembly (analogue adapters)
|
|
sbuf_t es_buf_a; // Audio packet reassembly
|
|
|
|
uint8_t *es_global_data;
|
|
int es_global_data_len;
|
|
int es_incomplete;
|
|
int es_ssc_intercept;
|
|
int es_ssc_ptr;
|
|
uint8_t es_ssc_buf[32];
|
|
|
|
struct th_pkt *es_curpkt;
|
|
int64_t es_curpts;
|
|
int64_t es_curdts;
|
|
int64_t es_prevdts;
|
|
int64_t es_nextdts;
|
|
int es_frame_duration;
|
|
int es_width;
|
|
int es_height;
|
|
|
|
int es_meta_change;
|
|
|
|
/* CA ID's on this stream */
|
|
struct caid_list es_caids;
|
|
|
|
int es_vbv_size; /* Video buffer size (in bytes) */
|
|
int es_vbv_delay; /* -1 if CBR */
|
|
|
|
/* */
|
|
|
|
int es_delete_me; /* Temporary flag for deleting streams */
|
|
|
|
/* Error log limiters */
|
|
|
|
tvhlog_limit_t es_cc_log;
|
|
tvhlog_limit_t es_pes_log;
|
|
|
|
char *es_nicename;
|
|
|
|
/* Teletext subtitle */
|
|
char es_blank; // Last subtitle was blank
|
|
|
|
/* SI section processing (horrible hack) */
|
|
void *es_section;
|
|
|
|
/* Filter temporary variable */
|
|
uint32_t es_filter;
|
|
|
|
} elementary_stream_t;
|
|
|
|
|
|
typedef TAILQ_HEAD(service_instance_list, service_instance) service_instance_list_t;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
typedef struct service_instance {
|
|
|
|
TAILQ_ENTRY(service_instance) si_link;
|
|
|
|
int si_prio;
|
|
|
|
struct service *si_s; // A reference is held
|
|
int si_instance; // Discriminator when having multiple adapters, etc
|
|
|
|
int si_error; /* Set if subscription layer deem this cand
|
|
to be broken. We typically set this if we
|
|
have not seen any demuxed packets after
|
|
the grace period has expired.
|
|
The actual value is current time
|
|
*/
|
|
|
|
time_t si_error_time;
|
|
|
|
|
|
int si_weight; // Highest weight that holds this cand
|
|
|
|
int si_mark; // For mark & sweep
|
|
|
|
char si_source[128];
|
|
|
|
} service_instance_t;
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
service_instance_t *service_instance_add(service_instance_list_t *sil,
|
|
struct service *s,
|
|
int instance,
|
|
const char *source,
|
|
int prio,
|
|
int weight);
|
|
|
|
void service_instance_destroy
|
|
(service_instance_list_t *sil, service_instance_t *si);
|
|
|
|
void service_instance_list_clear(service_instance_list_t *sil);
|
|
|
|
/**
|
|
*
|
|
*/
|
|
typedef struct service {
|
|
idnode_t s_id;
|
|
|
|
TAILQ_ENTRY(service) s_all_link;
|
|
|
|
enum {
|
|
/**
|
|
* Transport is idle.
|
|
*/
|
|
SERVICE_IDLE,
|
|
|
|
/**
|
|
* Transport producing output
|
|
*/
|
|
SERVICE_RUNNING,
|
|
|
|
/**
|
|
* Destroyed, but pointer is till valid.
|
|
* This would be the case if transport_destroy() did not actually free
|
|
* the transport because there are references held to it.
|
|
*
|
|
* Reference counts can be used so that code can hold a pointer to
|
|
* a transport without having the global lock.
|
|
*
|
|
* Note: No fields in the transport may be accessed without the
|
|
* global lock held. Thus, the global_lock must be reaquired and
|
|
* then s_status must be checked. If it is ZOMBIE the code must
|
|
* just drop the refcount and pretend that the transport never
|
|
* was there in the first place.
|
|
*/
|
|
SERVICE_ZOMBIE,
|
|
} s_status;
|
|
|
|
/**
|
|
* Refcount, operated using atomic.h ops.
|
|
*/
|
|
int s_refcount;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int s_flags;
|
|
|
|
#define S_DEBUG 0x1
|
|
|
|
/**
|
|
* Source type is used to determine if an output requesting
|
|
* MPEG-TS can shortcut all the parsing and remuxing.
|
|
*/
|
|
enum {
|
|
S_MPEG_TS,
|
|
S_MPEG_PS,
|
|
S_OTHER,
|
|
} s_source_type;
|
|
|
|
/**
|
|
* Service type
|
|
*/
|
|
enum {
|
|
ST_NONE,
|
|
ST_OTHER,
|
|
ST_SDTV,
|
|
ST_HDTV,
|
|
ST_RADIO
|
|
} s_servicetype;
|
|
|
|
// TODO: should this really be here?
|
|
|
|
/**
|
|
* PID carrying the programs PCR.
|
|
* XXX: We don't support transports that does not carry
|
|
* the PCR in one of the content streams.
|
|
*/
|
|
uint16_t s_pcr_pid;
|
|
|
|
/**
|
|
* PID for the PMT of this MPEG-TS stream.
|
|
*/
|
|
uint16_t s_pmt_pid;
|
|
|
|
/**
|
|
* Set if transport is enabled (the default). If disabled it should
|
|
* not be considered when chasing for available transports during
|
|
* subscription scheduling.
|
|
*/
|
|
int s_enabled;
|
|
|
|
LIST_ENTRY(service) s_active_link;
|
|
|
|
LIST_HEAD(, th_subscription) s_subscriptions;
|
|
|
|
int (*s_is_enabled)(struct service *t, int flags);
|
|
|
|
void (*s_enlist)(struct service *s, service_instance_list_t *sil, int flags);
|
|
|
|
int (*s_start_feed)(struct service *s, int instance);
|
|
|
|
void (*s_refresh_feed)(struct service *t);
|
|
|
|
void (*s_stop_feed)(struct service *t);
|
|
|
|
void (*s_config_save)(struct service *t);
|
|
|
|
void (*s_setsourceinfo)(struct service *t, struct source_info *si);
|
|
|
|
int (*s_grace_period)(struct service *t);
|
|
|
|
void (*s_delete)(struct service *t, int delconf);
|
|
|
|
/**
|
|
* Channel info
|
|
*/
|
|
int64_t (*s_channel_number) (struct service *);
|
|
const char *(*s_channel_name) (struct service *);
|
|
const char *(*s_provider_name) (struct service *);
|
|
const char *(*s_channel_icon) (struct service *);
|
|
|
|
/**
|
|
* Name usable for displaying to user
|
|
*/
|
|
char *s_nicename;
|
|
int s_nicename_prefidx;
|
|
|
|
/**
|
|
* Teletext...
|
|
*/
|
|
th_commercial_advice_t s_tt_commercial_advice;
|
|
time_t s_tt_clock; /* Network clock as determined by teletext decoder */
|
|
|
|
/**
|
|
* Channel mapping
|
|
*/
|
|
LIST_HEAD(,channel_service_mapping) s_channels;
|
|
|
|
/**
|
|
* Service mapping, see service_mapper.c form details
|
|
*/
|
|
int s_sm_onqueue;
|
|
TAILQ_ENTRY(service) s_sm_link;
|
|
|
|
/**
|
|
* Pending save.
|
|
*
|
|
* transport_request_save() will enqueue the transport here.
|
|
* We need to do this if we don't hold the global lock.
|
|
* This happens when we update PMT from within the TS stream itself.
|
|
* Then we hold the stream mutex, and thus, can not obtain the global lock
|
|
* as it would cause lock inversion.
|
|
*/
|
|
int s_ps_onqueue;
|
|
TAILQ_ENTRY(service) s_ps_link;
|
|
|
|
/**
|
|
* Timer which is armed at transport start. Once it fires
|
|
* it will check if any packets has been parsed. If not the status
|
|
* will be set to TRANSPORT_STATUS_NO_INPUT
|
|
*/
|
|
gtimer_t s_receive_timer;
|
|
/**
|
|
* Stream start time
|
|
*/
|
|
int s_timeout;
|
|
int s_grace_delay;
|
|
time_t s_start_time;
|
|
|
|
|
|
/*********************************************************
|
|
*
|
|
* Streaming part of transport
|
|
*
|
|
* All access to fields below this must be protected with
|
|
* s_stream_mutex held.
|
|
*
|
|
* Note: Code holding s_stream_mutex should _never_
|
|
* acquire global_lock while already holding s_stream_mutex.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Mutex to be held during streaming.
|
|
* This mutex also protects all elementary_stream_t instances for this
|
|
* transport.
|
|
*/
|
|
pthread_mutex_t s_stream_mutex;
|
|
|
|
|
|
/**
|
|
* Condition variable to singal when streaming_status changes
|
|
* interlocked with s_stream_mutex
|
|
*/
|
|
pthread_cond_t s_tss_cond;
|
|
/**
|
|
*
|
|
*/
|
|
int s_streaming_status;
|
|
|
|
// Progress
|
|
#define TSS_INPUT_HARDWARE 0x1
|
|
#define TSS_INPUT_SERVICE 0x2
|
|
#define TSS_MUX_PACKETS 0x4
|
|
#define TSS_PACKETS 0x8
|
|
#define TSS_NO_ACCESS 0x10
|
|
|
|
#define TSS_GRACEPERIOD 0x8000
|
|
|
|
// Errors
|
|
#define TSS_NO_DESCRAMBLER 0x10000
|
|
#define TSS_TIMEOUT 0x20000
|
|
|
|
#define TSS_ERRORS 0xffff0000
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
int s_streaming_live;
|
|
|
|
// Live status
|
|
#define TSS_LIVE 0x01
|
|
|
|
/**
|
|
* For simple streaming sources (such as video4linux) keeping
|
|
* track of the video and audio stream is convenient.
|
|
*/
|
|
#ifdef MOVE_TO_V4L
|
|
elementary_stream_t *s_video;
|
|
elementary_stream_t *s_audio;
|
|
#endif
|
|
|
|
/**
|
|
* Average bitrate
|
|
*/
|
|
avgstat_t s_rate;
|
|
|
|
/**
|
|
* Descrambling support
|
|
*/
|
|
|
|
struct th_descrambler_list s_descramblers;
|
|
uint16_t s_scrambled_seen;
|
|
th_descrambler_runtime_t *s_descramble;
|
|
|
|
/**
|
|
* List of all and filtered components.
|
|
*/
|
|
struct elementary_stream_queue s_components;
|
|
struct elementary_stream_queue s_filt_components;
|
|
int s_last_pid;
|
|
elementary_stream_t *s_last_es;
|
|
|
|
|
|
/**
|
|
* Delivery pad, this is were we finally deliver all streaming output
|
|
*/
|
|
streaming_pad_t s_streaming_pad;
|
|
|
|
tvhlog_limit_t s_tei_log;
|
|
|
|
int64_t s_current_pts;
|
|
|
|
} service_t;
|
|
|
|
|
|
|
|
|
|
|
|
void service_init(void);
|
|
void service_done(void);
|
|
|
|
int service_start(service_t *t, int instance, int timeout, int postpone);
|
|
void service_stop(service_t *t);
|
|
|
|
void service_build_filter(service_t *t);
|
|
|
|
service_t *service_create0(service_t *t, const idclass_t *idc, const char *uuid, int source_type, htsmsg_t *conf);
|
|
|
|
#define service_create(t, c, u, s, m)\
|
|
(struct t*)service_create0(calloc(1, sizeof(struct t), &t##_class, c, u, s, m)
|
|
|
|
void service_unref(service_t *t);
|
|
|
|
void service_ref(service_t *t);
|
|
|
|
static inline service_t *service_find(const char *identifier)
|
|
{ return idnode_find(identifier, &service_class, NULL); }
|
|
#define service_find_by_identifier service_find
|
|
|
|
service_instance_t *service_find_instance(struct service *s,
|
|
struct channel *ch,
|
|
service_instance_list_t *sil,
|
|
int *error, int weight,
|
|
int flags, int timeout,
|
|
int postpone);
|
|
|
|
elementary_stream_t *service_stream_find_(service_t *t, int pid);
|
|
|
|
static inline elementary_stream_t *
|
|
service_stream_find(service_t *t, int pid)
|
|
{
|
|
if (t->s_last_pid != (pid))
|
|
return service_stream_find_(t, pid);
|
|
else
|
|
return t->s_last_es;
|
|
}
|
|
|
|
elementary_stream_t *service_stream_create(service_t *t, int pid,
|
|
streaming_component_type_t type);
|
|
|
|
void service_settings_write(service_t *t);
|
|
|
|
const char *service_servicetype_txt(service_t *t);
|
|
|
|
int service_is_sdtv(service_t *t);
|
|
int service_is_hdtv(service_t *t);
|
|
int service_is_radio(service_t *t);
|
|
int service_is_other(service_t *t);
|
|
#define service_is_tv(s) (service_is_hdtv(s) || service_is_sdtv(s))
|
|
|
|
int service_is_encrypted ( service_t *t );
|
|
|
|
void service_destroy(service_t *t, int delconf);
|
|
|
|
void service_remove_subscriber(service_t *t, struct th_subscription *s,
|
|
int reason);
|
|
|
|
void service_set_streaming_status_flags_(service_t *t, int flag);
|
|
|
|
static inline void
|
|
service_set_streaming_status_flags(service_t *t, int flag)
|
|
{
|
|
int n = t->s_streaming_status;
|
|
if ((n & flag) != flag)
|
|
service_set_streaming_status_flags_(t, n | flag);
|
|
}
|
|
|
|
static inline void
|
|
service_reset_streaming_status_flags(service_t *t, int flag)
|
|
{
|
|
int n = t->s_streaming_status;
|
|
if ((n & flag) != 0)
|
|
service_set_streaming_status_flags_(t, n & ~flag);
|
|
}
|
|
|
|
struct streaming_start;
|
|
struct streaming_start *service_build_stream_start(service_t *t);
|
|
|
|
void service_restart(service_t *t, int had_components);
|
|
|
|
void service_stream_destroy(service_t *t, elementary_stream_t *st);
|
|
|
|
void service_request_save(service_t *t, int restart);
|
|
|
|
void service_source_info_free(source_info_t *si);
|
|
|
|
void service_source_info_copy(source_info_t *dst, const source_info_t *src);
|
|
|
|
void service_make_nicename(service_t *t);
|
|
|
|
const char *service_nicename(service_t *t);
|
|
|
|
const char *service_component_nicename(elementary_stream_t *st);
|
|
|
|
const char *service_adapter_nicename(service_t *t);
|
|
|
|
const char *service_tss2text(int flags);
|
|
|
|
static inline int service_tss_is_error(int flags)
|
|
{
|
|
return flags & TSS_ERRORS ? 1 : 0;
|
|
}
|
|
|
|
void service_refresh_channel(service_t *t);
|
|
|
|
int tss2errcode(int tss);
|
|
|
|
uint16_t service_get_encryption(service_t *t);
|
|
|
|
htsmsg_t *servicetype_list (void);
|
|
|
|
void service_load ( service_t *s, htsmsg_t *c );
|
|
|
|
void service_save ( service_t *s, htsmsg_t *c );
|
|
|
|
void sort_elementary_streams(service_t *t);
|
|
|
|
const char *service_get_channel_name (service_t *s);
|
|
const char *service_get_full_channel_name (service_t *s);
|
|
int64_t service_get_channel_number (service_t *s);
|
|
const char *service_get_channel_icon (service_t *s);
|
|
|
|
#endif // SERVICE_H__
|