Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
DVR: add possibility to re-record broken recordings
  • Loading branch information
perexg committed Oct 21, 2015
1 parent 9580e5e commit eb0c16d
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 3 deletions.
9 changes: 9 additions & 0 deletions src/dvr/dvr.h
Expand Up @@ -40,6 +40,7 @@ typedef struct dvr_config {
profile_t *dvr_profile;
char *dvr_storage;
int dvr_clone;
uint32_t dvr_rerecord_errors;
uint32_t dvr_retention_days;
uint32_t dvr_removal_days;
char *dvr_charset;
Expand Down Expand Up @@ -211,6 +212,12 @@ typedef struct dvr_entry {
*/
struct dvr_timerec_entry *de_timerec;

/**
* Parent/slave
*/
struct dvr_entry *de_parent;
struct dvr_entry *de_slave;

/**
* Fields for recording
*/
Expand Down Expand Up @@ -410,6 +417,8 @@ uint32_t dvr_entry_get_retention_days( dvr_entry_t *de );

uint32_t dvr_entry_get_removal_days( dvr_entry_t *de );

uint32_t dvr_entry_get_rerecord_errors( dvr_entry_t *de );

int dvr_entry_get_start_time( dvr_entry_t *de );

int dvr_entry_get_stop_time( dvr_entry_t *de );
Expand Down
7 changes: 7 additions & 0 deletions src/dvr/dvr_config.c
Expand Up @@ -830,6 +830,13 @@ const idclass_t dvr_config_class = {
.def.u32 = 1,
.group = 1,
},
{
.type = PT_U32,
.id = "rerecord-errors",
.name = N_("Re-record When Errors (0=off)"),
.off = offsetof(dvr_config_t, dvr_rerecord_errors),
.group = 1,
},
{
.type = PT_U32,
.id = "pre-extra-time",
Expand Down
206 changes: 203 additions & 3 deletions src/dvr/dvr_db.c
Expand Up @@ -41,11 +41,13 @@ static gtimer_t dvr_dbus_timer;
#endif

static void dvr_entry_destroy(dvr_entry_t *de, int delconf);
static void dvr_timer_rerecord(void *aux);
static void dvr_timer_expire(void *aux);
static void dvr_timer_remove_files(void *aux);
static void dvr_entry_start_recording(dvr_entry_t *de, int clone);
static void dvr_timer_start_recording(void *aux);
static void dvr_timer_stop_recording(void *aux);
static void dvr_entry_rerecord(dvr_entry_t *de);

/*
*
Expand Down Expand Up @@ -196,6 +198,12 @@ dvr_entry_get_removal_days ( dvr_entry_t *de )
return dvr_retention_cleanup(de->de_config->dvr_removal_days);
}

uint32_t
dvr_entry_get_rerecord_errors( dvr_entry_t *de )
{
return de->de_config->dvr_rerecord_errors;
}

/*
* DBUS next dvr start notifications
*/
Expand Down Expand Up @@ -234,6 +242,20 @@ dvr_dbus_timer_cb( void *aux )
}
#endif

/*
*
*/
static void
dvr_entry_retention_arm(dvr_entry_t *de, gti_callback_t *cb, time_t when)
{
uint32_t rerecord = dvr_entry_get_rerecord_errors(de);
if (rerecord && (when - dispatch_clock) > 3600) {
when = dispatch_clock + 3600;
cb = dvr_timer_rerecord;
}
gtimer_arm_abs(&de->de_timer, cb, de, when);
}

/*
*
*/
Expand All @@ -248,14 +270,14 @@ dvr_entry_retention_timer(dvr_entry_t *de)
stop = de->de_stop + removal * (time_t)86400;
if (removal > 0 || retention == 0) {
if (stop > dispatch_clock) {
gtimer_arm_abs(&de->de_timer, dvr_timer_remove_files, de, stop);
dvr_entry_retention_arm(de, dvr_timer_remove_files, stop);
return;
}
if (dvr_get_filename(de))
dvr_entry_delete(de, 1);
}
stop = de->de_stop + retention * (time_t)86400;
gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de, stop);
dvr_entry_retention_arm(de, dvr_timer_expire, stop);
}

/*
Expand Down Expand Up @@ -413,6 +435,8 @@ dvr_entry_set_timer(dvr_entry_t *de)
else
dvr_entry_completed(de, de->de_last_error);

dvr_entry_rerecord(de);

} else if (de->de_sched_state == DVR_RECORDING) {

gtimer_arm_abs(&de->de_timer, dvr_timer_stop_recording, de, stop);
Expand Down Expand Up @@ -755,6 +779,84 @@ dvr_entry_clone(dvr_entry_t *de)
return n == NULL ? de : n;
}

/**
*
*/
static void
dvr_entry_rerecord(dvr_entry_t *de)
{
uint32_t rerecord = dvr_entry_get_rerecord_errors(de);
epg_broadcast_t *e, *ev;
dvr_entry_t *de2;
char cfg_uuid[UUID_HEX_SIZE];
int64_t fsize1, fsize2;

if (rerecord == 0)
return;
if (de->de_parent) {
if (de->de_sched_state == DVR_COMPLETED &&
de->de_errors == 0 &&
de->de_data_errors < de->de_parent->de_data_errors) {
fsize1 = dvr_get_filesize(de);
fsize2 = dvr_get_filesize(de->de_parent);
if (fsize1 / 5 < fsize2 / 6) {
goto not_so_good;
} else {
dvr_entry_cancel_delete(de->de_parent);
}
} else if (de->de_sched_state == DVR_COMPLETED) {
not_so_good:
de->de_retention = 1;
de->de_parent->de_slave = NULL;
de->de_parent = NULL;
dvr_entry_completed(de, SM_CODE_WEAK_STREAM);
return;
}
}
if (de->de_slave)
return;
if (de->de_enabled == 0)
return;
if (de->de_channel == NULL)
return;
if (de->de_sched_state == DVR_SCHEDULED ||
de->de_sched_state == DVR_RECORDING)
return;
/* rerecord if - DVR_NOSTATE, DVR_MISSED_TIME */
if (de->de_sched_state == DVR_COMPLETED &&
rerecord < de->de_data_errors && de->de_errors <= 0)
return;

e = NULL;
RB_FOREACH(ev, &de->de_channel->ch_epg_schedule, sched_link) {
if (dvr_entry_fuzzy_match(de, ev))
if (!e || e->start > ev->start)
e = ev;
}

if (e == NULL)
return;

tvhtrace("dvr", " rerecord event %s on %s @ %"PRItime_t
" to %"PRItime_t,
epg_broadcast_get_title(e, NULL),
channel_get_name(e->channel),
e->start, e->stop);

idnode_uuid_as_str(&de->de_config->dvr_id, cfg_uuid);
de2 = dvr_entry_create_by_event(1, cfg_uuid, e,
de->de_start_extra, de->de_stop_extra,
de->de_owner, de->de_creator, NULL,
de->de_pri, de->de_retention, de->de_removal,
de->de_comment);
if (de2) {
de->de_slave = de2;
de2->de_parent = de;
dvr_entry_save(de);
dvr_entry_save(de2);
}
}

/**
*
*/
Expand Down Expand Up @@ -940,6 +1042,11 @@ dvr_entry_destroy(dvr_entry_t *de, int delconf)
LIST_REMOVE(de, de_global_link);
de->de_channel = NULL;

if (de->de_parent)
de->de_parent->de_slave = NULL;
if (de->de_slave)
de->de_slave->de_parent = NULL;

dvr_entry_dec_ref(de);
}

Expand Down Expand Up @@ -982,6 +1089,18 @@ dvr_entry_save(dvr_entry_t *de)
}


/**
*
*/
static void
dvr_timer_rerecord(void *aux)
{
dvr_entry_t *de = aux;
dvr_entry_rerecord(de);
dvr_entry_retention_timer(de);
}


/**
*
*/
Expand Down Expand Up @@ -1869,6 +1988,72 @@ dvr_entry_class_timerec_caption_get(void *o)
return &ret;
}

static int
dvr_entry_class_parent_set(void *o, const void *v)
{
dvr_entry_t *de = (dvr_entry_t *)o, *de2;
if (!dvr_entry_is_editable(de))
return 0;
de2 = v ? dvr_entry_find_by_uuid(v) : NULL;
if (de2 == NULL) {
if (de->de_parent) {
de->de_parent->de_slave = NULL;
de->de_parent = NULL;
return 1;
}
} else if (de->de_parent != de2) {
de->de_parent = de2;
de2->de_slave = de;
return 1;
}
return 0;
}

static const void *
dvr_entry_class_parent_get(void *o)
{
static const char *ret;
dvr_entry_t *de = (dvr_entry_t *)o;
if (de->de_parent)
ret = idnode_uuid_as_sstr(&de->de_parent->de_id);
else
ret = "";
return &ret;
}

static int
dvr_entry_class_slave_set(void *o, const void *v)
{
dvr_entry_t *de = (dvr_entry_t *)o, *de2;
if (!dvr_entry_is_editable(de))
return 0;
de2 = v ? dvr_entry_find_by_uuid(v) : NULL;
if (de2 == NULL) {
if (de->de_slave) {
de->de_slave->de_parent = NULL;
de->de_slave = NULL;
return 1;
}
} else if (de->de_slave != de2) {
de->de_slave = de2;
de2->de_parent = de;
return 1;
}
return 0;
}

static const void *
dvr_entry_class_slave_get(void *o)
{
static const char *ret;
dvr_entry_t *de = (dvr_entry_t *)o;
if (de->de_parent)
ret = idnode_uuid_as_sstr(&de->de_slave->de_id);
else
ret = "";
return &ret;
}

static int
dvr_entry_class_broadcast_set(void *o, const void *v)
{
Expand Down Expand Up @@ -2410,6 +2595,22 @@ const idclass_t dvr_entry_class = {
.get = dvr_entry_class_timerec_caption_get,
.opts = PO_RDONLY | PO_NOSAVE | PO_HIDDEN,
},
{
.type = PT_STR,
.id = "parent",
.name = N_("Parent Entry"),
.set = dvr_entry_class_parent_set,
.get = dvr_entry_class_parent_get,
.opts = PO_RDONLY,
},
{
.type = PT_STR,
.id = "slave",
.name = N_("Slave Entry"),
.set = dvr_entry_class_slave_set,
.get = dvr_entry_class_slave_get,
.opts = PO_RDONLY,
},
{
.type = PT_U32,
.id = "content_type",
Expand Down Expand Up @@ -2555,7 +2756,6 @@ dvr_val2pri(dvr_prio_t v)
return val2str(v, priotab) ?: "invalid";
}


/**
*
*/
Expand Down
2 changes: 2 additions & 0 deletions src/htsp_server.c
Expand Up @@ -3692,6 +3692,8 @@ _htsp_get_subscription_status(int smcode)
return "userAccess";
case SM_CODE_USER_LIMIT:
return "userLimit";
case SM_CODE_WEAK_STREAM:
return "weakStream";
default:
return streaming_code2txt(smcode);
}
Expand Down
2 changes: 2 additions & 0 deletions src/streaming.c
Expand Up @@ -422,6 +422,8 @@ streaming_code2txt(int code)
return N_("User access error");
case SM_CODE_USER_LIMIT:
return N_("User limit reached");
case SM_CODE_WEAK_STREAM:
return N_("Weak stream");

case SM_CODE_NO_FREE_ADAPTER:
return N_("No free adapter");
Expand Down
1 change: 1 addition & 0 deletions src/tvheadend.h
Expand Up @@ -442,6 +442,7 @@ typedef enum {
#define SM_CODE_INVALID_TARGET 104
#define SM_CODE_USER_ACCESS 105
#define SM_CODE_USER_LIMIT 106
#define SM_CODE_WEAK_STREAM 107

#define SM_CODE_NO_FREE_ADAPTER 200
#define SM_CODE_MUX_NOT_ENABLED 201
Expand Down

0 comments on commit eb0c16d

Please sign in to comment.