444 lines
10 KiB
C
444 lines
10 KiB
C
/*
|
|
* Tvheadend - structures
|
|
* Copyright (C) 2007 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 TVHEADEND_H
|
|
#define TVHEADEND_H
|
|
|
|
#include "config.h"
|
|
|
|
#include <pthread.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/time.h>
|
|
|
|
#include "queue.h"
|
|
#include "avg.h"
|
|
#include "hts_strtab.h"
|
|
|
|
#include "redblack.h"
|
|
|
|
#define PTS_UNSET INT64_C(0x8000000000000000)
|
|
|
|
extern pthread_mutex_t global_lock;
|
|
extern pthread_mutex_t ffmpeg_lock;
|
|
extern pthread_mutex_t fork_lock;
|
|
|
|
typedef struct source_info {
|
|
char *si_device;
|
|
char *si_adapter;
|
|
char *si_network;
|
|
char *si_mux;
|
|
char *si_provider;
|
|
char *si_service;
|
|
} source_info_t;
|
|
|
|
static inline void
|
|
lock_assert0(pthread_mutex_t *l, const char *file, int line)
|
|
{
|
|
if(pthread_mutex_trylock(l) == EBUSY)
|
|
return;
|
|
|
|
fprintf(stderr, "Mutex not held at %s:%d\n", file, line);
|
|
abort();
|
|
}
|
|
|
|
#define lock_assert(l) lock_assert0(l, __FILE__, __LINE__)
|
|
|
|
|
|
/*
|
|
* Commercial status
|
|
*/
|
|
typedef enum {
|
|
COMMERCIAL_UNKNOWN,
|
|
COMMERCIAL_YES,
|
|
COMMERCIAL_NO,
|
|
} th_commercial_advice_t;
|
|
|
|
|
|
/*
|
|
* global timer
|
|
*/
|
|
|
|
|
|
typedef void (gti_callback_t)(void *opaque);
|
|
|
|
typedef struct gtimer {
|
|
LIST_ENTRY(gtimer) gti_link;
|
|
gti_callback_t *gti_callback;
|
|
void *gti_opaque;
|
|
time_t gti_expire;
|
|
} gtimer_t;
|
|
|
|
void gtimer_arm(gtimer_t *gti, gti_callback_t *callback, void *opaque,
|
|
int delta);
|
|
|
|
void gtimer_arm_abs(gtimer_t *gti, gti_callback_t *callback, void *opaque,
|
|
time_t when);
|
|
|
|
void gtimer_disarm(gtimer_t *gti);
|
|
|
|
|
|
/*
|
|
* List / Queue header declarations
|
|
*/
|
|
LIST_HEAD(th_subscription_list, th_subscription);
|
|
RB_HEAD(channel_tree, channel);
|
|
TAILQ_HEAD(channel_queue, channel);
|
|
LIST_HEAD(channel_list, channel);
|
|
LIST_HEAD(event_list, event);
|
|
RB_HEAD(event_tree, event);
|
|
LIST_HEAD(dvr_config_list, dvr_config);
|
|
LIST_HEAD(dvr_entry_list, dvr_entry);
|
|
TAILQ_HEAD(ref_update_queue, ref_update);
|
|
LIST_HEAD(service_list, service);
|
|
RB_HEAD(service_tree, service);
|
|
TAILQ_HEAD(service_queue, service);
|
|
LIST_HEAD(elementary_stream_list, elementary_stream);
|
|
TAILQ_HEAD(elementary_stream_queue, elementary_stream);
|
|
LIST_HEAD(th_muxer_list, th_muxer);
|
|
LIST_HEAD(th_muxstream_list, th_muxstream);
|
|
LIST_HEAD(th_descrambler_list, th_descrambler);
|
|
TAILQ_HEAD(th_refpkt_queue, th_refpkt);
|
|
TAILQ_HEAD(th_muxpkt_queue, th_muxpkt);
|
|
LIST_HEAD(dvr_autorec_entry_list, dvr_autorec_entry);
|
|
TAILQ_HEAD(th_pktref_queue, th_pktref);
|
|
LIST_HEAD(streaming_target_list, streaming_target);
|
|
|
|
/**
|
|
* Log limiter
|
|
*/
|
|
typedef struct loglimter {
|
|
time_t last;
|
|
int events;
|
|
} loglimiter_t;
|
|
|
|
void limitedlog(loglimiter_t *ll, const char *sys,
|
|
const char *o, const char *event);
|
|
|
|
|
|
/**
|
|
* Device connection types
|
|
*/
|
|
#define HOSTCONNECTION_UNKNOWN 0
|
|
#define HOSTCONNECTION_USB12 1
|
|
#define HOSTCONNECTION_USB480 2
|
|
#define HOSTCONNECTION_PCI 3
|
|
|
|
const char *hostconnection2str(int type);
|
|
int get_device_connection(const char *dev);
|
|
|
|
|
|
/**
|
|
* Stream component types
|
|
*/
|
|
typedef enum {
|
|
SCT_UNKNOWN = 0,
|
|
SCT_MPEG2VIDEO = 1,
|
|
SCT_MPEG2AUDIO,
|
|
SCT_H264,
|
|
SCT_AC3,
|
|
SCT_TELETEXT,
|
|
SCT_DVBSUB,
|
|
SCT_CA,
|
|
SCT_PAT,
|
|
SCT_PMT,
|
|
SCT_AAC,
|
|
SCT_MPEGTS,
|
|
SCT_TEXTSUB,
|
|
SCT_EAC3,
|
|
SCT_MP4A,
|
|
} streaming_component_type_t;
|
|
|
|
#define SCT_ISVIDEO(t) ((t) == SCT_MPEG2VIDEO || (t) == SCT_H264)
|
|
#define SCT_ISAUDIO(t) ((t) == SCT_MPEG2AUDIO || (t) == SCT_AC3 || \
|
|
(t) == SCT_AAC || (t) == SCT_MP4A)
|
|
|
|
/**
|
|
* The signal status of a tuner
|
|
*/
|
|
typedef struct signal_status {
|
|
const char *status_text; /* adapter status text */
|
|
int snr; /* signal/noise ratio */
|
|
int signal; /* signal strength */
|
|
int ber; /* bit error rate */
|
|
int unc; /* uncorrected blocks */
|
|
} signal_status_t;
|
|
|
|
/**
|
|
* A streaming pad generates data.
|
|
* It has one or more streaming targets attached to it.
|
|
*
|
|
* We support two different streaming target types:
|
|
* One is callback driven and the other uses a queue + thread.
|
|
*
|
|
* Targets which already has a queueing intrastructure in place (such
|
|
* as HTSP) does not need any interim queues so it would be a waste. That
|
|
* is why we have the callback target.
|
|
*
|
|
*/
|
|
typedef struct streaming_pad {
|
|
struct streaming_target_list sp_targets;
|
|
int sp_ntargets;
|
|
} streaming_pad_t;
|
|
|
|
|
|
TAILQ_HEAD(streaming_message_queue, streaming_message);
|
|
|
|
/**
|
|
* Streaming messages types
|
|
*/
|
|
typedef enum {
|
|
/**
|
|
* Packet with data.
|
|
*
|
|
* sm_data points to a th_pkt. th_pkt will be unref'ed when
|
|
* the message is destroyed
|
|
*/
|
|
SMT_PACKET,
|
|
|
|
/**
|
|
* Stream start
|
|
*
|
|
* sm_data points to a stream_start struct.
|
|
* See transport_build_stream_start()
|
|
*/
|
|
|
|
SMT_START,
|
|
|
|
/**
|
|
* Service status
|
|
*
|
|
* Notification about status of source, see TSS_ flags
|
|
*/
|
|
SMT_SERVICE_STATUS,
|
|
|
|
/**
|
|
* Streaming stop.
|
|
*
|
|
* End of streaming. If sm_code is 0 this was a result to an
|
|
* unsubscription. Otherwise the reason was external and the
|
|
* subscription scheduler will attempt to start a new streaming
|
|
* session.
|
|
*/
|
|
SMT_STOP,
|
|
|
|
/**
|
|
* Streaming unable to start.
|
|
*
|
|
* sm_code indicates reason. Scheduler will try to restart
|
|
*/
|
|
SMT_NOSTART,
|
|
|
|
/**
|
|
* Raw MPEG TS data
|
|
*/
|
|
SMT_MPEGTS,
|
|
|
|
/**
|
|
* Internal message to exit receiver
|
|
*/
|
|
SMT_EXIT,
|
|
} streaming_message_type_t;
|
|
|
|
#define SMT_TO_MASK(x) (1 << ((unsigned int)x))
|
|
|
|
|
|
#define SM_CODE_OK 0
|
|
|
|
#define SM_CODE_UNDEFINED_ERROR 1
|
|
|
|
#define SM_CODE_SOURCE_RECONFIGURED 100
|
|
#define SM_CODE_BAD_SOURCE 101
|
|
#define SM_CODE_SOURCE_DELETED 102
|
|
#define SM_CODE_SUBSCRIPTION_OVERRIDDEN 103
|
|
|
|
#define SM_CODE_NO_HW_ATTACHED 200
|
|
#define SM_CODE_MUX_NOT_ENABLED 201
|
|
#define SM_CODE_NOT_FREE 202
|
|
#define SM_CODE_TUNING_FAILED 203
|
|
#define SM_CODE_SVC_NOT_ENABLED 204
|
|
#define SM_CODE_BAD_SIGNAL 205
|
|
#define SM_CODE_NO_SOURCE 206
|
|
#define SM_CODE_NO_SERVICE 207
|
|
|
|
#define SM_CODE_ABORTED 300
|
|
|
|
#define SM_CODE_NO_DESCRAMBLER 400
|
|
#define SM_CODE_NO_ACCESS 401
|
|
#define SM_CODE_NO_INPUT 402
|
|
|
|
/**
|
|
* Streaming messages are sent from the pad to its receivers
|
|
*/
|
|
typedef struct streaming_message {
|
|
TAILQ_ENTRY(streaming_message) sm_link;
|
|
streaming_message_type_t sm_type;
|
|
union {
|
|
void *sm_data;
|
|
int sm_code;
|
|
};
|
|
} streaming_message_t;
|
|
|
|
/**
|
|
* A streaming target receives data.
|
|
*/
|
|
|
|
typedef void (st_callback_t)(void *opauqe, streaming_message_t *sm);
|
|
|
|
typedef struct streaming_target {
|
|
LIST_ENTRY(streaming_target) st_link;
|
|
streaming_pad_t *st_pad; /* Source we are linked to */
|
|
|
|
st_callback_t *st_cb;
|
|
void *st_opaque;
|
|
int st_reject_filter;
|
|
} streaming_target_t;
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
typedef struct streaming_queue {
|
|
|
|
streaming_target_t sq_st;
|
|
|
|
pthread_mutex_t sq_mutex; /* Protects sp_queue */
|
|
pthread_cond_t sq_cond; /* Condvar for signalling new
|
|
packets */
|
|
|
|
struct streaming_message_queue sq_queue;
|
|
|
|
} streaming_queue_t;
|
|
|
|
|
|
/**
|
|
* Simple dynamically growing buffer
|
|
*/
|
|
typedef struct sbuf {
|
|
uint8_t *sb_data;
|
|
int sb_ptr;
|
|
int sb_size;
|
|
int sb_err;
|
|
} sbuf_t;
|
|
|
|
|
|
|
|
const char *streaming_component_type2txt(streaming_component_type_t s);
|
|
|
|
static inline unsigned int tvh_strhash(const char *s, unsigned int mod)
|
|
{
|
|
unsigned int v = 5381;
|
|
while(*s)
|
|
v += (v << 5) + v + *s++;
|
|
return v % mod;
|
|
}
|
|
|
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
|
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
|
|
|
void tvh_str_set(char **strp, const char *src);
|
|
int tvh_str_update(char **strp, const char *src);
|
|
|
|
void tvhlog(int severity, const char *subsys, const char *fmt, ...);
|
|
|
|
void tvhlog_spawn(int severity, const char *subsys, const char *fmt, ...);
|
|
|
|
#define LOG_EMERG 0 /* system is unusable */
|
|
#define LOG_ALERT 1 /* action must be taken immediately */
|
|
#define LOG_CRIT 2 /* critical conditions */
|
|
#define LOG_ERR 3 /* error conditions */
|
|
#define LOG_WARNING 4 /* warning conditions */
|
|
#define LOG_NOTICE 5 /* normal but significant condition */
|
|
#define LOG_INFO 6 /* informational */
|
|
#define LOG_DEBUG 7 /* debug-level messages */
|
|
|
|
extern int log_debug;
|
|
|
|
#define DEBUGLOG(subsys, fmt...) do { \
|
|
if(log_debug) \
|
|
tvhlog(LOG_DEBUG, subsys, fmt); \
|
|
} while(0)
|
|
|
|
|
|
static inline int64_t
|
|
getmonoclock(void)
|
|
{
|
|
struct timespec tp;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &tp);
|
|
|
|
return tp.tv_sec * 1000000ULL + (tp.tv_nsec / 1000);
|
|
}
|
|
|
|
int sri_to_rate(int sri);
|
|
int rate_to_sri(int rate);
|
|
|
|
|
|
extern time_t dispatch_clock;
|
|
extern struct service_list all_transports;
|
|
extern struct channel_tree channel_name_tree;
|
|
|
|
extern void scopedunlock(pthread_mutex_t **mtxp);
|
|
|
|
#define scopedlock(mtx) \
|
|
pthread_mutex_t *scopedlock ## __LINE__ \
|
|
__attribute__((cleanup(scopedunlock))) = mtx; \
|
|
pthread_mutex_lock(mtx);
|
|
|
|
#define scopedgloballock() scopedlock(&global_lock)
|
|
|
|
#define tvh_strdupa(n) ({ int tvh_l = strlen(n); \
|
|
char *tvh_b = alloca(tvh_l + 1); \
|
|
memcpy(tvh_b, n, tvh_l + 1); })
|
|
|
|
#define tvh_strlcatf(buf, size, fmt...) \
|
|
snprintf((buf) + strlen(buf), (size) - strlen(buf), fmt)
|
|
|
|
int tvh_open(const char *pathname, int flags, mode_t mode);
|
|
|
|
int tvh_socket(int domain, int type, int protocol);
|
|
|
|
void hexdump(const char *pfx, const uint8_t *data, int len);
|
|
|
|
uint32_t crc32(uint8_t *data, size_t datalen, uint32_t crc);
|
|
|
|
int base64_decode(uint8_t *out, const char *in, int out_size);
|
|
|
|
int put_utf8(char *out, int c);
|
|
|
|
static inline int64_t ts_rescale(int64_t ts, int tb)
|
|
{
|
|
// return (ts * tb + (tb / 2)) / 90000LL;
|
|
return (ts * tb ) / 90000LL;
|
|
}
|
|
|
|
void sbuf_free(sbuf_t *sb);
|
|
|
|
void sbuf_reset(sbuf_t *sb);
|
|
|
|
void sbuf_err(sbuf_t *sb);
|
|
|
|
void sbuf_alloc(sbuf_t *sb, int len);
|
|
|
|
void sbuf_append(sbuf_t *sb, const uint8_t *data, int len);
|
|
|
|
void sbuf_cut(sbuf_t *sb, int off);
|
|
|
|
#endif /* TV_HEAD_H */
|