]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
DVR: an attempt to add the EPG running state check, fixes #3228
authorJaroslav Kysela <perex@perex.cz>
Fri, 30 Oct 2015 17:22:50 +0000 (18:22 +0100)
committerJaroslav Kysela <perex@perex.cz>
Fri, 30 Oct 2015 17:22:56 +0000 (18:22 +0100)
src/dvr/dvr.h
src/dvr/dvr_config.c
src/dvr/dvr_db.c
src/dvr/dvr_rec.c
src/epg.c
src/epg.h
src/epggrab/module/eit.c

index 1439580dfeb779f2674903867fb02d7e36068d4b..0b65a877fbea66152be86769908db7e900e15c8a 100644 (file)
@@ -49,6 +49,7 @@ typedef struct dvr_config {
   uint32_t dvr_extra_time_pre;
   uint32_t dvr_extra_time_post;
   uint32_t dvr_update_window;
+  int dvr_running;
 
   muxer_config_t dvr_muxcnf;
 
@@ -111,6 +112,7 @@ typedef enum {
   DVR_RS_RUNNING,
   DVR_RS_COMMERCIAL,
   DVR_RS_ERROR,
+  DVR_RS_EPG_WAIT,
 } dvr_rs_state_t;
   
 
@@ -150,6 +152,9 @@ typedef struct dvr_entry {
   time_t de_start_extra;
   time_t de_stop_extra;
 
+  time_t de_running_start;
+  time_t de_running_stop;
+
   char *de_owner;
   char *de_creator;
   char *de_comment;
@@ -421,13 +426,13 @@ 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 );
+time_t dvr_entry_get_start_time( dvr_entry_t *de );
 
-int dvr_entry_get_stop_time( dvr_entry_t *de );
+time_t dvr_entry_get_stop_time( dvr_entry_t *de );
 
-int dvr_entry_get_extra_time_post( dvr_entry_t *de );
+time_t dvr_entry_get_extra_time_post( dvr_entry_t *de );
 
-int dvr_entry_get_extra_time_pre( dvr_entry_t *de );
+time_t dvr_entry_get_extra_time_pre( dvr_entry_t *de );
 
 void dvr_entry_init(void);
 
@@ -501,6 +506,8 @@ void dvr_event_removed(epg_broadcast_t *e);
 
 void dvr_event_updated(epg_broadcast_t *e);
 
+void dvr_event_running(epg_broadcast_t *e, epg_source_t esrc, int running);
+
 dvr_entry_t *dvr_entry_find_by_id(int id);
 
 static inline dvr_entry_t *dvr_entry_find_by_uuid(const char *uuid)
index 1b70d124454499f4bc4ad994d2a771e0c6644cd8..203c35fbefd7cb95592d3f03fc06ad7a3f1f2ecb 100644 (file)
@@ -182,6 +182,7 @@ dvr_config_create(const char *name, const char *uuid, htsmsg_t *conf)
   cfg->dvr_skip_commercials = 1;
   dvr_charset_update(cfg, intlconv_filesystem_charset());
   cfg->dvr_update_window = 24 * 3600;
+  cfg->dvr_running = 1;
   cfg->dvr_pathname = strdup("$t$n.$x");
 
   /* Muxer config */
@@ -862,6 +863,14 @@ const idclass_t dvr_config_class = {
       .def.u32  = 24*3600,
       .group    = 1,
     },
+    {
+      .type     = PT_BOOL,
+      .id       = "epg-running",
+      .name     = N_("Use EPG Running State"),
+      .off      = offsetof(dvr_config_t, dvr_running),
+      .def.u32  = 1,
+      .group    = 1,
+    },
     {
       .type     = PT_STR,
       .id       = "postproc",
index 2c6198c6db77ea8ad9ed7138bafe14b76556ada5..685d5ac6c5dbddcb9c5019671e9004f2280f2ad6 100644 (file)
@@ -204,20 +204,20 @@ static inline int extra_valid(time_t extra)
   return extra != 0 && extra != (time_t)-1;
 }
 
-int
+time_t
 dvr_entry_get_start_time( dvr_entry_t *de )
 {
   /* Note 30 seconds might not be enough (rotors) */
   return de->de_start - (60 * dvr_entry_get_extra_time_pre(de)) - 30;
 }
 
-int
+time_t
 dvr_entry_get_stop_time( dvr_entry_t *de )
 {
   return de->de_stop + (60 * dvr_entry_get_extra_time_post(de));
 }
 
-int
+time_t
 dvr_entry_get_extra_time_pre( dvr_entry_t *de )
 {
   time_t extra = de->de_start_extra;
@@ -233,7 +233,7 @@ dvr_entry_get_extra_time_pre( dvr_entry_t *de )
   return extra;
 }
 
-int
+time_t
 dvr_entry_get_extra_time_post( dvr_entry_t *de )
 {
   time_t extra = de->de_stop_extra;
@@ -419,6 +419,8 @@ dvr_entry_status(dvr_entry_t *de)
       return N_("Commercial break");
     case DVR_RS_ERROR:
       return streaming_code2txt(de->de_last_error);
+    case DVR_RS_EPG_WAIT:
+      return N_("Waiting for EPG running flag");
     default:
       return N_("Invalid");
     }
@@ -513,7 +515,7 @@ dvr_usage_count(access_t *aa)
   return used;
 }
 
-static void
+void
 dvr_entry_set_timer(dvr_entry_t *de)
 {
   time_t now, start, stop;
@@ -528,6 +530,12 @@ dvr_entry_set_timer(dvr_entry_t *de)
 
   if (now >= stop || de->de_dont_reschedule) {
 
+    /* EPG thinks that the program is running */
+    if(de->de_running_start > de->de_running_stop) {
+      stop = dispatch_clock + 10;
+      goto recording;
+    }
+
     if(htsmsg_is_empty(de->de_files))
       dvr_entry_missed_time(de, de->de_last_error);
     else
@@ -538,6 +546,7 @@ dvr_entry_set_timer(dvr_entry_t *de)
 
   } else if (de->de_sched_state == DVR_RECORDING)  {
 
+recording:
     gtimer_arm_abs(&de->de_timer, dvr_timer_stop_recording, de, stop);
 
   } else if (de->de_channel && de->de_channel->ch_enabled) {
@@ -1537,6 +1546,58 @@ void dvr_event_updated(epg_broadcast_t *e)
   }
 }
 
+/**
+ * Event running status is updated
+ */
+void dvr_event_running(epg_broadcast_t *e, epg_source_t esrc, int running)
+{
+  dvr_entry_t *de;
+
+  if (esrc != EPG_SOURCE_EIT)
+    return;
+  de = dvr_entry_find_by_event(e);
+  if (de == NULL)
+    return;
+  if (!de->de_config->dvr_running) {
+    de->de_running_start = de->de_running_stop = 0;
+    return;
+  }
+  if (running) {
+    if (!de->de_running_start)
+      tvhdebug("dvr", "dvr entry %s event %s on %s - EPG marking start",
+               idnode_uuid_as_sstr(&de->de_id),
+               epg_broadcast_get_title(e, NULL),
+               channel_get_name(e->channel));
+    de->de_running_start = dispatch_clock;
+    if (dvr_entry_get_start_time(de) > dispatch_clock) {
+      de->de_start = dispatch_clock;
+      dvr_entry_set_timer(de);
+      tvhdebug("dvr", "dvr entry %s event %s on %s - EPG start",
+               idnode_uuid_as_sstr(&de->de_id),
+               epg_broadcast_get_title(e, NULL),
+               channel_get_name(e->channel));
+    }
+  } else {
+    if (!de->de_running_stop ||
+        de->de_running_start > de->de_running_stop)
+      tvhdebug("dvr", "dvr entry %s event %s on %s - EPG marking stop",
+               idnode_uuid_as_sstr(&de->de_id),
+               epg_broadcast_get_title(e, NULL),
+               channel_get_name(e->channel));
+    de->de_running_stop = dispatch_clock;
+    if (de->de_sched_state == DVR_RECORDING) {
+      if (dvr_entry_get_stop_time(de) > dispatch_clock) {
+        de->de_dont_reschedule = 1;
+        dvr_entry_set_timer(de);
+        tvhdebug("dvr", "dvr entry %s event %s on %s - EPG stop",
+               idnode_uuid_as_sstr(&de->de_id),
+               epg_broadcast_get_title(e, NULL),
+               channel_get_name(e->channel));
+      }
+    }
+  }
+}
+
 /**
  *
  */
@@ -1582,6 +1643,12 @@ dvr_stop_recording(dvr_entry_t *de, int stopcode, int saveconf, int clone)
 static void
 dvr_timer_stop_recording(void *aux)
 {
+  dvr_entry_t *de = aux;
+  /* EPG thinks that the program is running */
+  if (de->de_running_start > de->de_running_stop) {
+    gtimer_arm(&de->de_timer, dvr_timer_stop_recording, de, 10);
+    return;
+  }
   dvr_stop_recording(aux, SM_CODE_OK, 1, 0);
 }
 
index 44f62dc2e89f6cee04b17b55ef8f2743e04ea399..ba584bbb8e98ae23a81c30c5c207034c6422e98c 100644 (file)
@@ -1047,8 +1047,9 @@ dvr_thread(void *aux)
   streaming_message_t *sm;
   th_subscription_t *ts;
   th_pkt_t *pkt;
-  int run = 1, started = 0, comm_skip;
+  int run = 1, started = 0, comm_skip, epg_running, rs;
   int commercial = COMMERCIAL_UNKNOWN;
+  int64_t packets = 0;
   char *postproc;
 
   pthread_mutex_lock(&global_lock);
@@ -1089,42 +1090,57 @@ dvr_thread(void *aux)
     /* we don't want to start new recordings at this point */
     if (sm->sm_type == SMT_START && de->de_thread_shutdown)
       break;
+    epg_running = de->de_running_start > de->de_running_stop ||
+                  (de->de_running_start == 0 && de->de_running_stop == 0);
     pthread_mutex_unlock(&sq->sq_mutex);
 
     switch(sm->sm_type) {
 
     case SMT_PACKET:
       pkt = sm->sm_data;
-      if(pkt->pkt_commercial == COMMERCIAL_YES)
-       dvr_rec_set_state(de, DVR_RS_COMMERCIAL, 0);
-      else
-       dvr_rec_set_state(de, DVR_RS_RUNNING, 0);
 
-      if(pkt->pkt_commercial == COMMERCIAL_YES && comm_skip)
+      rs = DVR_RS_RUNNING;
+      if (!epg_running)
+        rs = DVR_RS_EPG_WAIT;
+      else if (pkt->pkt_commercial == COMMERCIAL_YES)
+        rs = DVR_RS_COMMERCIAL;
+      dvr_rec_set_state(de, rs, 0);
+
+      if (rs == DVR_RS_COMMERCIAL && comm_skip)
        break;
+      if (!epg_running)
+        break;
 
-      if(commercial != pkt->pkt_commercial)
+      if (commercial != pkt->pkt_commercial)
        muxer_add_marker(prch->prch_muxer);
 
       commercial = pkt->pkt_commercial;
 
-      if(started) {
+      if (started) {
+       if (!epg_running && packets)
+         goto restart;
        muxer_write_pkt(prch->prch_muxer, sm->sm_type, sm->sm_data);
        sm->sm_data = NULL;
        dvr_notify(de);
+       packets++;
       }
       break;
 
     case SMT_MPEGTS:
       if(started) {
-       dvr_rec_set_state(de, DVR_RS_RUNNING, 0);
+       dvr_rec_set_state(de, !epg_running ? DVR_RS_EPG_WAIT : DVR_RS_RUNNING, 0);
+       if (!epg_running && packets)
+         goto restart;
        muxer_write_pkt(prch->prch_muxer, sm->sm_type, sm->sm_data);
        sm->sm_data = NULL;
        dvr_notify(de);
+       packets++;
       }
       break;
 
     case SMT_START:
+restart:
+      packets = 0;
       if(started &&
         muxer_reconfigure(prch->prch_muxer, sm->sm_data) < 0) {
        tvhlog(LOG_WARNING,
@@ -1135,10 +1151,12 @@ dvr_thread(void *aux)
        // support reconfiguration of the streams.
        dvr_thread_epilog(de, postproc);
        started = 0;
-       pthread_mutex_lock(&global_lock);
-       if (de->de_config->dvr_clone)
-         de = dvr_entry_clone(de);
-       pthread_mutex_unlock(&global_lock);
+       if (epg_running) {
+         pthread_mutex_lock(&global_lock);
+         if (de->de_config->dvr_clone)
+           de = dvr_entry_clone(de);
+         pthread_mutex_unlock(&global_lock);
+        }
       }
 
       if(!started) {
index bb915dd01c7478b6654070e1f1e47b09741608c3..e3ed6db7abdd702e3a4cd18a2bc21f467247dba3 100644 (file)
--- a/src/epg.c
+++ b/src/epg.c
@@ -1417,8 +1417,14 @@ static void _epg_channel_timer_callback ( void *p )
   channel_t *ch = (channel_t*)p;
 
   /* Clear now/next */
-  if ((cur = ch->ch_epg_now))
+  if ((cur = ch->ch_epg_now)) {
+    if (cur->running) {
+      /* running? don't do anything */
+      gtimer_arm(&ch->ch_epg_timer, _epg_channel_timer_callback, ch, 2);
+      return;
+    }
     cur->getref(cur);
+  }
   if ((nxt = ch->ch_epg_next))
     nxt->getref(nxt);
   ch->ch_epg_now = ch->ch_epg_next = NULL;
@@ -1676,6 +1682,26 @@ epg_broadcast_t *epg_broadcast_find_by_eid ( channel_t *ch, uint16_t eid )
   return NULL;
 }
 
+void epg_broadcast_notify_running
+  ( epg_broadcast_t *broadcast, epg_source_t esrc, int running )
+{
+  channel_t *ch;
+  epg_broadcast_t *now;
+
+  broadcast->running = !!running;
+  if (!running) {
+    broadcast->stop = dispatch_clock - 1;
+  } else {
+    ch = broadcast->channel;
+    now = ch ? ch->ch_epg_now : NULL;
+    if (broadcast != now && now) {
+      now->running = 0;
+      dvr_event_running(ch->ch_epg_now, esrc, 0);
+    }
+  }
+  dvr_event_running(broadcast, esrc, running);
+}
+
 int epg_broadcast_set_episode 
   ( epg_broadcast_t *broadcast, epg_episode_t *episode, epggrab_module_t *src )
 {
index 7abdbe61cdc48abaa46b0cd320243e7a71d1f34e..9a06fbfe97346dff3eea07e1c3ea986c8f8ffeca 100644 (file)
--- a/src/epg.h
+++ b/src/epg.h
@@ -56,6 +56,14 @@ typedef struct epg_serieslink      epg_serieslink_t;
 
 extern int epg_in_load;
 
+/*
+ *
+ */
+typedef enum {
+  EPG_SOURCE_NONE = 0,
+  EPG_SOURCE_EIT = 1,
+} epg_source_t;
+
 /* ************************************************************************
  * Genres
  * ***********************************************************************/
@@ -436,6 +444,7 @@ struct epg_broadcast
   /* Misc flags */
   uint8_t                    is_new;           ///< New series / file premiere
   uint8_t                    is_repeat;        ///< Repeat screening
+  uint8_t                    running;          ///< EPG running flag
 
   /* Broadcast level text */
   lang_str_t                *summary;          ///< Summary
@@ -460,6 +469,8 @@ epg_broadcast_t *epg_broadcast_find_by_id  ( uint32_t id );
 /* Special */
 epg_broadcast_t *epg_broadcast_clone
   ( struct channel *channel, epg_broadcast_t *src, int *save );
+void epg_broadcast_notify_running
+  ( epg_broadcast_t *b, epg_source_t esrc, int running );
 
 /* Mutators */
 int epg_broadcast_set_episode
index fb516f51e55356b304cf49b213dc288ae9014682..3314c9cc46cfb25c395fc4467986b7b79f7c0c06 100644 (file)
@@ -26,6 +26,7 @@
 #include "epggrab/private.h"
 #include "input.h"
 #include "input/mpegts/dvb_charset.h"
+#include "dvr/dvr.h"
 
 /* ************************************************************************
  * Status handling
@@ -396,16 +397,15 @@ static int _eit_desc_crid
  * ***********************************************************************/
 
 static int _eit_process_event_one
-  ( epggrab_module_t *mod, int tableid,
+  ( epggrab_module_t *mod, int tableid, int sect,
     mpegts_service_t *svc, channel_t *ch,
     const uint8_t *ptr, int len,
     int local, int *resched, int *save )
 {
-  int save2 = 0;
-  int dllen;
+  int dllen, save2 = 0;
   time_t start, stop;
   uint16_t eid;
-  uint8_t dtag, dlen;
+  uint8_t dtag, dlen, running;
   epg_broadcast_t *ebc;
   epg_episode_t *ee;
   epg_serieslink_t *es;
@@ -417,6 +417,7 @@ static int _eit_process_event_one
   stop  = start + bcdtoint(ptr[7] & 0xff) * 3600 +
                   bcdtoint(ptr[8] & 0xff) * 60 +
                   bcdtoint(ptr[9] & 0xff);
+  running = (ptr[10] >> 5) & 0x07;
   dllen = ((ptr[10] & 0x0f) << 8) | ptr[11];
 
   len -= 12;
@@ -546,11 +547,15 @@ static int _eit_process_event_one
   if (ev.summary) lang_str_destroy(ev.summary);
   if (ev.desc)    lang_str_destroy(ev.desc);
 
+  /* use running flag only for current broadcast */
+  if (running && tableid == 0x4e && sect == 0)
+    epg_broadcast_notify_running(ebc, EPG_SOURCE_EIT, running == 4);
+
   return 0;
 }
 
 static int _eit_process_event
-  ( epggrab_module_t *mod, int tableid,
+  ( epggrab_module_t *mod, int tableid, int sect,
     mpegts_service_t *svc, const uint8_t *ptr, int len,
     int local, int *resched, int *save )
 {
@@ -562,7 +567,7 @@ static int _eit_process_event
   LIST_FOREACH(ilm, &svc->s_channels, ilm_in1_link) {
     ch = (channel_t *)ilm->ilm_in2;
     if (!ch->ch_enabled || ch->ch_epg_parent) continue;
-    if (_eit_process_event_one(mod, tableid, svc, ch,
+    if (_eit_process_event_one(mod, tableid, sect, svc, ch,
                                ptr, len, local, resched, save) < 0)
       return -1;
   }
@@ -683,7 +688,7 @@ _eit_callback
   ptr += 11;
   while (len) {
     int r;
-    if ((r = _eit_process_event(mod, tableid, svc, ptr, len,
+    if ((r = _eit_process_event(mod, tableid, sect, svc, ptr, len,
                                 mm->mm_network->mn_localtime,
                                 &resched, &save)) < 0)
       break;