601 lines
14 KiB
C
601 lines
14 KiB
C
/*
|
|
* Digital Video Recorder
|
|
* Copyright (C) 2008 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 DVR_H
|
|
#define DVR_H
|
|
|
|
#include <regex.h>
|
|
#include "epg.h"
|
|
#include "channels.h"
|
|
#include "subscriptions.h"
|
|
#include "muxer.h"
|
|
#include "profile.h"
|
|
#include "lang_str.h"
|
|
|
|
typedef struct dvr_config {
|
|
idnode_t dvr_id;
|
|
|
|
LIST_ENTRY(dvr_config) config_link;
|
|
LIST_ENTRY(dvr_config) profile_link;
|
|
|
|
int dvr_enabled;
|
|
int dvr_valid;
|
|
char *dvr_config_name;
|
|
char *dvr_comment;
|
|
profile_t *dvr_profile;
|
|
char *dvr_storage;
|
|
uint32_t dvr_retention_days;
|
|
char *dvr_charset;
|
|
char *dvr_charset_id;
|
|
char *dvr_postproc;
|
|
uint32_t dvr_extra_time_pre;
|
|
uint32_t dvr_extra_time_post;
|
|
uint32_t dvr_update_window;
|
|
|
|
muxer_config_t dvr_muxcnf;
|
|
|
|
int dvr_dir_per_day;
|
|
int dvr_channel_dir;
|
|
int dvr_channel_in_title;
|
|
int dvr_omit_title;
|
|
int dvr_date_in_title;
|
|
int dvr_time_in_title;
|
|
int dvr_whitespace_in_title;
|
|
int dvr_title_dir;
|
|
int dvr_episode_in_title;
|
|
int dvr_clean_title;
|
|
int dvr_tag_files;
|
|
int dvr_skip_commercials;
|
|
int dvr_subtitle_in_title;
|
|
int dvr_episode_before_date;
|
|
int dvr_episode_duplicate;
|
|
|
|
/* Series link support */
|
|
int dvr_sl_brand_lock;
|
|
int dvr_sl_season_lock;
|
|
int dvr_sl_channel_lock;
|
|
int dvr_sl_time_lock;
|
|
int dvr_sl_more_recent;
|
|
int dvr_sl_quality_lock;
|
|
|
|
/* Duplicate detect */
|
|
int dvr_dup_detect_episode;
|
|
|
|
struct dvr_entry_list dvr_entries;
|
|
struct dvr_autorec_entry_list dvr_autorec_entries;
|
|
struct dvr_timerec_entry_list dvr_timerec_entries;
|
|
|
|
struct access_entry_list dvr_accesses;
|
|
|
|
} dvr_config_t;
|
|
|
|
extern struct dvr_config_list dvrconfigs;
|
|
|
|
extern struct dvr_entry_list dvrentries;
|
|
|
|
typedef enum {
|
|
DVR_PRIO_IMPORTANT = 0,
|
|
DVR_PRIO_HIGH = 1,
|
|
DVR_PRIO_NORMAL = 2,
|
|
DVR_PRIO_LOW = 3,
|
|
DVR_PRIO_UNIMPORTANT = 4,
|
|
DVR_PRIO_NOTSET = 5,
|
|
} dvr_prio_t;
|
|
|
|
|
|
LIST_HEAD(dvr_rec_stream_list, dvr_rec_stream);
|
|
|
|
|
|
typedef enum {
|
|
DVR_SCHEDULED, /* Scheduled for recording (in the future) */
|
|
DVR_RECORDING,
|
|
DVR_COMPLETED, /* If recording failed, de->de_error is set to
|
|
a string */
|
|
DVR_NOSTATE,
|
|
DVR_MISSED_TIME,
|
|
} dvr_entry_sched_state_t;
|
|
|
|
|
|
typedef enum {
|
|
DVR_RS_PENDING,
|
|
DVR_RS_WAIT_PROGRAM_START,
|
|
DVR_RS_RUNNING,
|
|
DVR_RS_COMMERCIAL,
|
|
DVR_RS_ERROR,
|
|
} dvr_rs_state_t;
|
|
|
|
|
|
typedef struct dvr_entry {
|
|
|
|
idnode_t de_id;
|
|
|
|
int de_refcnt; /* Modification is protected under global_lock */
|
|
|
|
|
|
/**
|
|
* Upon dvr_entry_remove() this fields will be invalidated (and pointers
|
|
* NULLed)
|
|
*/
|
|
|
|
LIST_ENTRY(dvr_entry) de_global_link;
|
|
|
|
channel_t *de_channel;
|
|
LIST_ENTRY(dvr_entry) de_channel_link;
|
|
|
|
char *de_channel_name;
|
|
|
|
gtimer_t de_timer;
|
|
|
|
/**
|
|
* These meta fields will stay valid as long as reference count > 0
|
|
*/
|
|
|
|
dvr_config_t *de_config;
|
|
LIST_ENTRY(dvr_entry) de_config_link;
|
|
|
|
time_t de_start;
|
|
time_t de_stop;
|
|
|
|
time_t de_start_extra;
|
|
time_t de_stop_extra;
|
|
|
|
char *de_creator;
|
|
char *de_comment;
|
|
char *de_filename; /* Initially null if no filename has been
|
|
generated yet */
|
|
lang_str_t *de_title; /* Title in UTF-8 (from EPG) */
|
|
lang_str_t *de_desc; /* Description in UTF-8 (from EPG) */
|
|
uint32_t de_content_type; /* Content type (from EPG) (only code) */
|
|
|
|
uint16_t de_dvb_eid;
|
|
|
|
int de_pri;
|
|
int de_dont_reschedule;
|
|
int de_mc;
|
|
int de_retention;
|
|
|
|
/**
|
|
* EPG information / links
|
|
*/
|
|
epg_broadcast_t *de_bcast;
|
|
char *de_episode;
|
|
|
|
/**
|
|
* Major State
|
|
*/
|
|
dvr_entry_sched_state_t de_sched_state;
|
|
|
|
/**
|
|
* Recording state (onyl valid if de_sched_state == DVR_RECORDING)
|
|
*/
|
|
dvr_rs_state_t de_rec_state;
|
|
|
|
/**
|
|
* Number of errors (only to be modified by the recording thread)
|
|
*/
|
|
uint32_t de_errors;
|
|
|
|
/**
|
|
* Last error, see SM_CODE_ defines
|
|
*/
|
|
uint32_t de_last_error;
|
|
|
|
|
|
/**
|
|
* Autorec linkage
|
|
*/
|
|
LIST_ENTRY(dvr_entry) de_autorec_link;
|
|
struct dvr_autorec_entry *de_autorec;
|
|
|
|
/**
|
|
* Timerec linkage
|
|
*/
|
|
struct dvr_timerec_entry *de_timerec;
|
|
|
|
/**
|
|
* Fields for recording
|
|
*/
|
|
pthread_t de_thread;
|
|
|
|
th_subscription_t *de_s;
|
|
|
|
/**
|
|
* Stream worker chain
|
|
*/
|
|
profile_chain_t *de_chain;
|
|
|
|
/**
|
|
* Inotify
|
|
*/
|
|
#if ENABLE_INOTIFY
|
|
LIST_ENTRY(dvr_entry) de_inotify_link;
|
|
#endif
|
|
|
|
} dvr_entry_t;
|
|
|
|
#define DVR_CH_NAME(e) ((e)->de_channel == NULL ? (e)->de_channel_name : channel_get_name((e)->de_channel))
|
|
|
|
/**
|
|
* Autorec entry
|
|
*/
|
|
typedef struct dvr_autorec_entry {
|
|
idnode_t dae_id;
|
|
|
|
TAILQ_ENTRY(dvr_autorec_entry) dae_link;
|
|
|
|
char *dae_name;
|
|
dvr_config_t *dae_config;
|
|
LIST_ENTRY(dvr_autorec_entry) dae_config_link;
|
|
|
|
int dae_enabled;
|
|
char *dae_creator;
|
|
char *dae_comment;
|
|
|
|
char *dae_title;
|
|
regex_t dae_title_preg;
|
|
|
|
uint32_t dae_content_type;
|
|
|
|
int dae_start; /* Minutes from midnight */
|
|
|
|
uint32_t dae_weekdays;
|
|
|
|
channel_t *dae_channel;
|
|
LIST_ENTRY(dvr_autorec_entry) dae_channel_link;
|
|
|
|
channel_tag_t *dae_channel_tag;
|
|
LIST_ENTRY(dvr_autorec_entry) dae_channel_tag_link;
|
|
|
|
int dae_pri;
|
|
|
|
struct dvr_entry_list dae_spawns;
|
|
|
|
epg_brand_t *dae_brand;
|
|
epg_season_t *dae_season;
|
|
epg_serieslink_t *dae_serieslink;
|
|
epg_episode_num_t dae_epnum;
|
|
|
|
int dae_minduration;
|
|
int dae_maxduration;
|
|
int dae_retention;
|
|
|
|
time_t dae_start_extra;
|
|
time_t dae_stop_extra;
|
|
} dvr_autorec_entry_t;
|
|
|
|
TAILQ_HEAD(dvr_autorec_entry_queue, dvr_autorec_entry);
|
|
|
|
extern struct dvr_autorec_entry_queue autorec_entries;
|
|
|
|
/**
|
|
* Timerec entry
|
|
*/
|
|
typedef struct dvr_timerec_entry {
|
|
idnode_t dte_id;
|
|
|
|
TAILQ_ENTRY(dvr_timerec_entry) dte_link;
|
|
|
|
char *dte_name;
|
|
dvr_config_t *dte_config;
|
|
LIST_ENTRY(dvr_timerec_entry) dte_config_link;
|
|
|
|
int dte_enabled;
|
|
char *dte_creator;
|
|
char *dte_comment;
|
|
|
|
char *dte_title;
|
|
|
|
int dte_start; /* Minutes from midnight */
|
|
int dte_stop; /* Minutes from midnight */
|
|
|
|
uint32_t dte_weekdays;
|
|
|
|
channel_t *dte_channel;
|
|
LIST_ENTRY(dvr_timerec_entry) dte_channel_link;
|
|
|
|
int dte_pri;
|
|
|
|
dvr_entry_t *dte_spawn;
|
|
|
|
int dte_retention;
|
|
} dvr_timerec_entry_t;
|
|
|
|
TAILQ_HEAD(dvr_timerec_entry_queue, dvr_timerec_entry);
|
|
|
|
extern struct dvr_timerec_entry_queue timerec_entries;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
|
|
extern const idclass_t dvr_config_class;
|
|
extern const idclass_t dvr_entry_class;
|
|
extern const idclass_t dvr_autorec_entry_class;
|
|
extern const idclass_t dvr_timerec_entry_class;
|
|
|
|
/**
|
|
* Prototypes
|
|
*/
|
|
|
|
static inline int dvr_config_is_valid(dvr_config_t *cfg)
|
|
{ return cfg->dvr_valid; }
|
|
|
|
static inline int dvr_config_is_default(dvr_config_t *cfg)
|
|
{ return cfg->dvr_config_name == NULL || cfg->dvr_config_name[0] == '\0'; }
|
|
|
|
dvr_config_t *dvr_config_find_by_name(const char *name);
|
|
|
|
dvr_config_t *dvr_config_find_by_name_default(const char *name);
|
|
|
|
dvr_config_t *dvr_config_find_by_list(htsmsg_t *list, const char *name);
|
|
|
|
dvr_config_t *dvr_config_create(const char *name, const char *uuid, htsmsg_t *conf);
|
|
|
|
static inline dvr_config_t *dvr_config_find_by_uuid(const char *uuid)
|
|
{ return (dvr_config_t*)idnode_find(uuid, &dvr_config_class, NULL); }
|
|
|
|
void dvr_config_delete(const char *name);
|
|
|
|
void dvr_config_save(dvr_config_t *cfg);
|
|
|
|
void dvr_config_destroy_by_profile(profile_t *pro, int delconf);
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
void dvr_make_title(char *output, size_t outlen, dvr_entry_t *de);
|
|
|
|
static inline int dvr_entry_is_editable(dvr_entry_t *de)
|
|
{ return de->de_sched_state == DVR_SCHEDULED; }
|
|
|
|
static inline int dvr_entry_is_valid(dvr_entry_t *de)
|
|
{ return de->de_refcnt > 0; }
|
|
|
|
int dvr_entry_get_mc(dvr_entry_t *de);
|
|
|
|
int dvr_entry_get_retention( dvr_entry_t *de );
|
|
|
|
int dvr_entry_get_start_time( dvr_entry_t *de );
|
|
|
|
int dvr_entry_get_stop_time( dvr_entry_t *de );
|
|
|
|
int dvr_entry_get_extra_time_post( dvr_entry_t *de );
|
|
|
|
int dvr_entry_get_extra_time_pre( dvr_entry_t *de );
|
|
|
|
void dvr_entry_init(void);
|
|
|
|
void dvr_entry_done(void);
|
|
|
|
void dvr_entry_save(dvr_entry_t *de);
|
|
|
|
void dvr_entry_destroy_by_config(dvr_config_t *cfg, int delconf);
|
|
|
|
const char *dvr_entry_status(dvr_entry_t *de);
|
|
|
|
const char *dvr_entry_schedstatus(dvr_entry_t *de);
|
|
|
|
void dvr_entry_create_by_autorec(epg_broadcast_t *e, dvr_autorec_entry_t *dae);
|
|
|
|
void dvr_entry_created(dvr_entry_t *de);
|
|
|
|
dvr_entry_t *
|
|
dvr_entry_create ( const char *uuid, htsmsg_t *conf );
|
|
|
|
|
|
dvr_entry_t *
|
|
dvr_entry_create_by_event( const char *dvr_config_uuid,
|
|
epg_broadcast_t *e,
|
|
time_t start_extra, time_t stop_extra,
|
|
const char *creator,
|
|
dvr_autorec_entry_t *dae,
|
|
dvr_prio_t pri, int retention,
|
|
const char *comment );
|
|
|
|
dvr_entry_t *
|
|
dvr_entry_create_htsp( const char *dvr_config_uuid,
|
|
channel_t *ch, time_t start, time_t stop,
|
|
time_t start_extra, time_t stop_extra,
|
|
const char *title, const char *description,
|
|
const char *lang, epg_genre_t *content_type,
|
|
const char *creator, dvr_autorec_entry_t *dae,
|
|
dvr_prio_t pri, int retention,
|
|
const char *comment );
|
|
|
|
dvr_entry_t *
|
|
dvr_entry_update( dvr_entry_t *de,
|
|
const char* de_title, const char *de_desc, const char *lang,
|
|
time_t de_start, time_t de_stop,
|
|
time_t de_start_extra, time_t de_stop_extra,
|
|
dvr_prio_t pri, int retention );
|
|
|
|
void dvr_destroy_by_channel(channel_t *ch, int delconf);
|
|
|
|
void dvr_rec_subscribe(dvr_entry_t *de);
|
|
|
|
void dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode);
|
|
|
|
void dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e);
|
|
|
|
void dvr_event_updated(epg_broadcast_t *e);
|
|
|
|
dvr_entry_t *dvr_entry_find_by_id(int id);
|
|
|
|
static inline dvr_entry_t *dvr_entry_find_by_uuid(const char *uuid)
|
|
{ return (dvr_entry_t*)idnode_find(uuid, &dvr_entry_class, NULL); }
|
|
|
|
dvr_entry_t *dvr_entry_find_by_event(epg_broadcast_t *e);
|
|
|
|
dvr_entry_t *dvr_entry_find_by_event_fuzzy(epg_broadcast_t *e);
|
|
|
|
dvr_entry_t *dvr_entry_find_by_episode(epg_broadcast_t *e);
|
|
|
|
int64_t dvr_get_filesize(dvr_entry_t *de);
|
|
|
|
dvr_entry_t *dvr_entry_cancel(dvr_entry_t *de);
|
|
|
|
void dvr_entry_dec_ref(dvr_entry_t *de);
|
|
|
|
void dvr_entry_delete(dvr_entry_t *de);
|
|
|
|
void dvr_entry_cancel_delete(dvr_entry_t *de);
|
|
|
|
htsmsg_t *dvr_entry_class_mc_list (void *o);
|
|
htsmsg_t *dvr_entry_class_pri_list(void *o);
|
|
htsmsg_t *dvr_entry_class_config_name_list(void *o);
|
|
htsmsg_t *dvr_entry_class_duration_list(void *o, const char *not_set, int max, int step);
|
|
|
|
/**
|
|
*
|
|
*/
|
|
|
|
dvr_autorec_entry_t *
|
|
dvr_autorec_create(const char *uuid, htsmsg_t *conf);
|
|
|
|
dvr_entry_t *
|
|
dvr_entry_create_(const char *config_uuid, epg_broadcast_t *e,
|
|
channel_t *ch, time_t start, time_t stop,
|
|
time_t start_extra, time_t stop_extra,
|
|
const char *title, const char *description,
|
|
const char *lang, epg_genre_t *content_type,
|
|
const char *creator, dvr_autorec_entry_t *dae,
|
|
dvr_timerec_entry_t *tae,
|
|
dvr_prio_t pri, int retention, const char *comment);
|
|
|
|
dvr_autorec_entry_t *
|
|
dvr_autorec_create_htsp(const char *dvr_config_name, const char *title,
|
|
channel_t *ch, uint32_t aroundTime, uint32_t days,
|
|
time_t start_extra, time_t stop_extra,
|
|
dvr_prio_t pri, int retention,
|
|
int min_duration, int max_duration,
|
|
const char *creator, const char *comment);
|
|
|
|
dvr_autorec_entry_t *
|
|
dvr_autorec_add_series_link(const char *dvr_config_name,
|
|
epg_broadcast_t *event,
|
|
const char *creator, const char *comment);
|
|
|
|
void dvr_autorec_save(dvr_autorec_entry_t *dae);
|
|
|
|
void dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge);
|
|
|
|
static inline dvr_autorec_entry_t *
|
|
dvr_autorec_find_by_uuid(const char *uuid)
|
|
{ return (dvr_autorec_entry_t*)idnode_find(uuid, &dvr_autorec_entry_class, NULL); }
|
|
|
|
|
|
htsmsg_t * dvr_autorec_entry_class_time_list(void *o, const char *null);
|
|
htsmsg_t * dvr_autorec_entry_class_weekdays_get(uint32_t weekdays);
|
|
htsmsg_t * dvr_autorec_entry_class_weekdays_list ( void *o );
|
|
char * dvr_autorec_entry_class_weekdays_rend(uint32_t weekdays);
|
|
|
|
void dvr_autorec_check_event(epg_broadcast_t *e);
|
|
void dvr_autorec_check_brand(epg_brand_t *b);
|
|
void dvr_autorec_check_season(epg_season_t *s);
|
|
void dvr_autorec_check_serieslink(epg_serieslink_t *s);
|
|
|
|
void autorec_destroy_by_config(dvr_config_t *cfg, int delconf);
|
|
|
|
void autorec_destroy_by_channel(channel_t *ch, int delconf);
|
|
|
|
void autorec_destroy_by_channel_tag(channel_tag_t *ct, int delconf);
|
|
|
|
void autorec_destroy_by_id(const char *id, int delconf);
|
|
|
|
void dvr_autorec_init(void);
|
|
|
|
void dvr_autorec_done(void);
|
|
|
|
void dvr_autorec_update(void);
|
|
|
|
/**
|
|
*
|
|
*/
|
|
|
|
dvr_timerec_entry_t *
|
|
dvr_timerec_create(const char *uuid, htsmsg_t *conf);
|
|
|
|
static inline dvr_timerec_entry_t *
|
|
dvr_timerec_find_by_uuid(const char *uuid)
|
|
{ return (dvr_timerec_entry_t*)idnode_find(uuid, &dvr_timerec_entry_class, NULL); }
|
|
|
|
|
|
void dvr_timerec_save(dvr_timerec_entry_t *dae);
|
|
|
|
void dvr_timerec_check(dvr_timerec_entry_t *dae);
|
|
|
|
void timerec_destroy_by_config(dvr_config_t *cfg, int delconf);
|
|
|
|
void timerec_destroy_by_channel(channel_t *ch, int delconf);
|
|
|
|
void timerec_destroy_by_id(const char *id, int delconf);
|
|
|
|
void dvr_timerec_init(void);
|
|
|
|
void dvr_timerec_done(void);
|
|
|
|
void dvr_timerec_update(void);
|
|
|
|
/**
|
|
*
|
|
*/
|
|
dvr_prio_t dvr_pri2val(const char *s);
|
|
|
|
const char *dvr_val2pri(dvr_prio_t v);
|
|
|
|
/**
|
|
* Inotify support
|
|
*/
|
|
void dvr_inotify_init ( void );
|
|
void dvr_inotify_done ( void );
|
|
void dvr_inotify_add ( dvr_entry_t *de );
|
|
void dvr_inotify_del ( dvr_entry_t *de );
|
|
|
|
/**
|
|
* Cutpoints support
|
|
**/
|
|
|
|
typedef struct dvr_cutpoint {
|
|
TAILQ_ENTRY(dvr_cutpoint) dc_link;
|
|
uint64_t dc_start_ms;
|
|
uint64_t dc_end_ms;
|
|
enum {
|
|
DVR_CP_CUT,
|
|
DVR_CP_MUTE,
|
|
DVR_CP_SCENE,
|
|
DVR_CP_COMM
|
|
} dc_type;
|
|
} dvr_cutpoint_t;
|
|
|
|
typedef TAILQ_HEAD(,dvr_cutpoint) dvr_cutpoint_list_t;
|
|
|
|
dvr_cutpoint_list_t *dvr_get_cutpoint_list (dvr_entry_t *de);
|
|
void dvr_cutpoint_list_destroy (dvr_cutpoint_list_t *list);
|
|
|
|
/**
|
|
*
|
|
*/
|
|
|
|
void dvr_init(void);
|
|
void dvr_config_init(void);
|
|
|
|
void dvr_done(void);
|
|
|
|
|
|
#endif /* DVR_H */
|