static void dvr_entry_destroy(dvr_entry_t *de, int delconf);
static void dvr_timer_expire(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);
* Create the event from config
*/
dvr_entry_t *
-dvr_entry_create(const char *uuid, htsmsg_t *conf)
+dvr_entry_create(const char *uuid, htsmsg_t *conf, int clone)
{
dvr_entry_t *de, *de2;
int64_t start, stop;
LIST_INSERT_HEAD(&dvrentries, de, de_global_link);
- if (de->de_channel) {
+ if (de->de_channel && !de->de_dont_reschedule && !clone) {
LIST_FOREACH(de2, &de->de_channel->ch_dvrs, de_channel_link)
if(de2 != de &&
de2->de_config == de->de_config &&
}
}
- dvr_entry_set_timer(de);
+ if (!clone)
+ dvr_entry_set_timer(de);
htsp_dvr_entry_add(de);
return de;
htsmsg_add_str(conf, "directory", dte->dte_directory ?: "");
}
- de = dvr_entry_create(NULL, conf);
+ de = dvr_entry_create(NULL, conf, 0);
htsmsg_destroy(conf);
comment);
}
+/**
+ * Clone existing DVR entry and stop the previous
+ */
+dvr_entry_t *
+dvr_entry_clone(dvr_entry_t *de)
+{
+ htsmsg_t *conf = htsmsg_create_map();
+ dvr_entry_t *n;
+
+ lock_assert(&global_lock);
+
+ idnode_save(&de->de_id, conf);
+ n = dvr_entry_create(NULL, conf, 1);
+ htsmsg_destroy(conf);
+
+ if (n) {
+ if(de->de_sched_state == DVR_RECORDING) {
+ de->de_dont_reschedule = 1;
+ dvr_stop_recording(de, SM_CODE_SOURCE_RECONFIGURED, 1, 1);
+ dvr_rec_migrate(de, n);
+ n->de_start = dispatch_clock;
+ dvr_entry_start_recording(n, 1);
+ } else {
+ dvr_entry_set_timer(n);
+ }
+ dvr_entry_save(n);
+ }
+
+ return n == NULL ? de : n;
+}
+
/**
*
*/
*
*/
void
-dvr_stop_recording(dvr_entry_t *de, int stopcode, int saveconf)
+dvr_stop_recording(dvr_entry_t *de, int stopcode, int saveconf, int clone)
{
dvr_rs_state_t rec_state = de->de_rec_state;
- dvr_rec_unsubscribe(de);
+ if (!clone)
+ dvr_rec_unsubscribe(de);
if (stopcode != SM_CODE_INVALID_TARGET &&
(rec_state == DVR_RS_PENDING ||
static void
dvr_timer_stop_recording(void *aux)
{
- dvr_stop_recording(aux, SM_CODE_OK, 1);
+ dvr_stop_recording(aux, SM_CODE_OK, 1, 0);
}
*
*/
static void
-dvr_timer_start_recording(void *aux)
+dvr_entry_start_recording(dvr_entry_t *de, int clone)
{
- dvr_entry_t *de = aux;
int r;
- if (de->de_channel == NULL || !de->de_channel->ch_enabled) {
- dvr_entry_set_state(de, DVR_NOSTATE, DVR_RS_PENDING, de->de_last_error);
- return;
- }
-
- // if duplicate, then delete it now, don't record!
- if (_dvr_duplicate_event(de)) {
- dvr_entry_cancel_delete(de);
- return;
- }
-
dvr_entry_set_state(de, DVR_RECORDING, DVR_RS_PENDING, SM_CODE_OK);
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" recorder starting",
lang_str_get(de->de_title, NULL), DVR_CH_NAME(de));
- if ((r = dvr_rec_subscribe(de)) < 0) {
+ if (!clone && (r = dvr_rec_subscribe(de)) < 0) {
dvr_entry_completed(de, r == -EPERM ? SM_CODE_USER_ACCESS :
(r == -EOVERFLOW ? SM_CODE_USER_LIMIT :
SM_CODE_BAD_SOURCE));
}
+/**
+ *
+ */
+static void
+dvr_timer_start_recording(void *aux)
+{
+ dvr_entry_t *de = aux;
+
+ if (de->de_channel == NULL || !de->de_channel->ch_enabled) {
+ dvr_entry_set_state(de, DVR_NOSTATE, DVR_RS_PENDING, de->de_last_error);
+ return;
+ }
+
+ // if duplicate, then delete it now, don't record!
+ if (_dvr_duplicate_event(de)) {
+ dvr_entry_cancel_delete(de);
+ return;
+ }
+
+ dvr_entry_start_recording(de, 0);
+}
+
+
/**
*
*/
dvr_entry_purge(dvr_entry_t *de, int delconf)
{
if(de->de_sched_state == DVR_RECORDING)
- dvr_stop_recording(de, SM_CODE_SOURCE_DELETED, delconf);
+ dvr_stop_recording(de, SM_CODE_SOURCE_DELETED, delconf, 0);
}
dvr_entry_stop(dvr_entry_t *de)
{
if(de->de_sched_state == DVR_RECORDING) {
- dvr_stop_recording(de, SM_CODE_OK, 1);
+ dvr_stop_recording(de, SM_CODE_OK, 1, 0);
return de;
}
- return dvr_entry_cancel(de);
+ return de;
}
/**
case DVR_RECORDING:
de->de_dont_reschedule = 1;
- dvr_stop_recording(de, SM_CODE_ABORTED, 1);
+ dvr_stop_recording(de, SM_CODE_ABORTED, 1, 0);
return de;
case DVR_COMPLETED:
case DVR_RECORDING:
de->de_dont_reschedule = 1;
- dvr_stop_recording(de, SM_CODE_ABORTED, 1);
+ dvr_stop_recording(de, SM_CODE_ABORTED, 1, 0);
case DVR_COMPLETED:
dvr_entry_delete(de);
break;
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
- (void)dvr_entry_create(f->hmf_name, c);
+ (void)dvr_entry_create(f->hmf_name, c, 0);
}
htsmsg_destroy(l);
}
assert(prch != NULL);
streaming_target_deliver(prch->prch_st, streaming_msg_create(SMT_EXIT));
-
+
pthread_join(de->de_thread, NULL);
subscription_unsubscribe(de->de_s, 0);
free(prch);
}
+/**
+ *
+ */
+void
+dvr_rec_migrate(dvr_entry_t *de_old, dvr_entry_t *de_new)
+{
+ lock_assert(&global_lock);
+
+ de_new->de_s = de_old->de_s;
+ de_new->de_chain = de_old->de_chain;
+ de_new->de_thread = de_old->de_thread;
+ de_old->de_s = NULL;
+ de_old->de_chain = NULL;
+ de_old->de_thread = 0;
+}
/**
* Replace various chars with a dash
dvr_thread(void *aux)
{
dvr_entry_t *de = aux;
- dvr_config_t *cfg = de->de_config;
profile_chain_t *prch = de->de_chain;
streaming_queue_t *sq = &prch->prch_sq;
streaming_message_t *sm;
th_subscription_t *ts;
th_pkt_t *pkt;
- int run = 1;
- int started = 0;
- int comm_skip = cfg->dvr_skip_commercials;
+ int run = 1, started = 0, comm_skip;
int commercial = COMMERCIAL_UNKNOWN;
+ pthread_mutex_lock(&global_lock);
+ comm_skip = de->de_config->dvr_skip_commercials;
+ pthread_mutex_unlock(&global_lock);
+
pthread_mutex_lock(&sq->sq_mutex);
while(run) {
// support reconfiguration of the streams.
dvr_thread_epilog(de);
started = 0;
+ pthread_mutex_lock(&global_lock);
+ if (de->de_config->dvr_clone)
+ de = dvr_entry_clone(de);
+ pthread_mutex_unlock(&global_lock);
}
if(!started) {
if(dvr_rec_start(de, sm->sm_data) == 0)
started = 1;
else
- dvr_stop_recording(de, SM_CODE_INVALID_TARGET, 1);
+ dvr_stop_recording(de, SM_CODE_INVALID_TARGET, 1, 0);
pthread_mutex_unlock(&global_lock);
}
break;