From: Jaroslav Kysela Date: Wed, 21 Oct 2015 14:06:10 +0000 (+0200) Subject: DVR: add possibility to re-record broken recordings X-Git-Tag: v4.2.1~1847 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=eb0c16d31d47865657fba3be378fdd89bb2b8a2d;p=thirdparty%2Ftvheadend.git DVR: add possibility to re-record broken recordings --- diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 966b0be1b..af047b6b3 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -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; @@ -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 */ @@ -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 ); diff --git a/src/dvr/dvr_config.c b/src/dvr/dvr_config.c index d74c5a7f5..9e5222b65 100644 --- a/src/dvr/dvr_config.c +++ b/src/dvr/dvr_config.c @@ -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", diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index a3647bd97..64e1c76b5 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -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); /* * @@ -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 */ @@ -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); +} + /* * */ @@ -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); } /* @@ -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); @@ -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); + } +} + /** * */ @@ -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); } @@ -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); +} + + /** * */ @@ -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) { @@ -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", @@ -2555,7 +2756,6 @@ dvr_val2pri(dvr_prio_t v) return val2str(v, priotab) ?: "invalid"; } - /** * */ diff --git a/src/htsp_server.c b/src/htsp_server.c index 99cbd9cc9..efe3bfb0a 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -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); } diff --git a/src/streaming.c b/src/streaming.c index b0b53f053..6b86640ad 100644 --- a/src/streaming.c +++ b/src/streaming.c @@ -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"); diff --git a/src/tvheadend.h b/src/tvheadend.h index c1bbf26b0..5f2dbee94 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -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