[mythtv] [PATCH] Changing channels in the background

Chris Bagwell chris at cnpbagwell.com
Wed Mar 2 03:13:35 UTC 2005


Hi all,

I've just set up my first mythtv box using Mythtv 0.71 and getting 
confortable with it.  My only complaint so far is that changing channels 
takes to long (I'm a channel surfer I guess).  Changing channels takes 
between 5-8 seconds, usually closer to 8 seconds.  For reference, I've 
got an EPIA-M(tvout)+IVTV+256Mram setup.

So I'm researching things that could speed up changing channels.  The 
first thing I noticed was that running my external changer script takes 
~3 seconds.  I've got a dish network receiver and so I have to use an IR 
blaster and have around 0.3 pauses between button pushes.  This adds up 
to seconds fast. Example log:

2005-02-28 18:59:13.991 External channel change: 
/home/mythtv/changechannel.sh 21
2005-02-28 18:59:14.005 Waiting for External Tuning program to exit
2005-02-28 18:59:15.236 ret_pid(0) child(6508) status(0x0)
2005-02-28 18:59:16.476 ret_pid(0) child(6508) status(0x0)
2005-02-28 18:59:17.519 ret_pid(6508) child(6508) status(0x0)
2005-02-28 18:59:17.522 External Tuning program no longer running
2005-02-28 18:59:17.525 External Tuning program exited with no error

Mythtv is currently waiting for the external script to complete before 
doing more work.  It would seem that running the script in the 
background would be a better approach so that mythtv can be doing some 
concurrent work while the script does its "sleep 0.3".

So I've coded up a quick proof of concept to do this.  It does shave a 
second or two off change changes in my tests.  I've included the patch 
below.

I'd like to get some feedback on if there are reasons that this isn't 
the current behaviour and other things like any issues with using 
VERBOSE() from a forked process.  If its of interested, I'd be glad to 
clean it up some and resend (for example, kinda overkill to do the 
timeout check on the first fork now).

Next up, I'm hoping some recent cvs commits to ivtvdecoder.cpp will help 
speed up channel changes as well.  I'll be testing that out next.

Gist of patch: Do a double fork() and immediately exit from first 
fork(). This will deamon-ize the external channel changer.  Had to do a 
third fork() to allow the timeout cleanup code to keep working.

Chris
-------------- next part --------------
*** ../../../orig/mythtv-0.17/libs/libmythtv/channelbase.cpp	2005-02-02 01:40:28.000000000 -0600
--- channelbase.cpp	2005-02-28 19:19:54.000000000 -0600
***************
*** 179,184 ****
--- 179,275 ----
      }
      else if (child == 0)
      {   // we are the new fork
+         pid_t deamon_pid = fork();
+ 
+         if (deamon_pid < 0)
+         {
+             _exit(1);
+         }
+         else if (deamon_pid == 0)
+         {
+             pid_t deamon_child = fork();
+ 
+             if (deamon_child < 0)
+             {   // error encountered in creating fork
+                 QString msg("ChannelBase: fork error -- ");
+                 msg.append(strerror(errno));
+                 VERBOSE(VB_IMPORTANT, msg);
+                 return false;
+             }
+             else if (deamon_child == 0)
+             {
+                 for(int i = 3; i < sysconf(_SC_OPEN_MAX) - 1; ++i)
+                     close(i);
+                 int ret = execl("/bin/sh", "sh", "-c", command.ascii(), NULL);
+                 QString msg("ChannelBase: ");
+                 if (EACCES == ret) {
+                    msg.append(QString("Access denied to /bin/sh"
+                                   " when executing %1\n").arg(command.ascii()));
+                }
+                msg.append(strerror(errno));
+                VERBOSE(VB_IMPORTANT, msg);
+                _exit(1); // this exit is ok, we are just exiting from the channel changing fork with an error.
+            }
+            else
+            {   // deamon_child contains the pid of the new process
+                int status = 0, pid = 0;
+                VERBOSE(VB_CHANNEL, 
+                        "Waiting for External Tuning program to exit");
+ 
+                 bool timed_out = false;
+                 uint timeout = 30; // how long to wait in seconds
+                 time_t start_time = time(0);
+                 while (-1 != pid && !timed_out)
+                 {
+                     sleep(1);
+                     pid = waitpid(deamon_child, &status, WUNTRACED|WNOHANG);
+                     VERBOSE(VB_IMPORTANT, QString("ret_pid(%1) deamon_child(%2) status(0x%3)")
+                         .arg(pid).arg(deamon_child).arg(status,0,16));
+                     if (pid==deamon_child)
+                         break;
+                     else if (time(0) > (time_t)(start_time + timeout))
+                         timed_out = true;
+                 }
+                 if (timed_out)
+                 {
+                     VERBOSE(VB_IMPORTANT, "External Tuning program timed out, killing");
+                     kill(deamon_child, SIGTERM);
+                     usleep(500);
+                     kill(deamon_child, SIGKILL);
+                     return false;
+                 }
+ 
+                 VERBOSE(VB_CHANNEL, "External Tuning program no longer running");
+                 if (WIFEXITED(status))
+                 {   // program exited normally
+                     int ret = WEXITSTATUS(status);
+                     if (ret)
+                     {   // external tuning program returned error value
+                         VERBOSE(VB_IMPORTANT,
+                                 QString("ChannelBase: external tuning program "
+                                 "exited with error %1").arg(ret));
+                         return false;
+                     }
+                     VERBOSE(VB_IMPORTANT, "External Tuning program exited with no error");
+                 }
+                 else
+                 {   // program exited abnormally
+                     QString msg = QString("ChannelBase: external tuning program "
+                                           "encountered error %1 -- ").arg(errno);
+                     msg.append(strerror(errno));
+                     VERBOSE(VB_IMPORTANT, msg);
+                     return false;
+                 } 
+              } // (deamon_child > 0)
+          } // if (deamon == 0)
+         else
+         {
+             // We are the parent so return with success.  This will
+             // cause child to be owned by the "init" process and
+             // so no one needs to clean up after it.
+             _exit(0);
+         }
+ 
          for(int i = 3; i < sysconf(_SC_OPEN_MAX) - 1; ++i)
              close(i);
          int ret = execl("/bin/sh", "sh", "-c", command.ascii(), NULL);
***************
*** 194,200 ****
      else
      {   // child contains the pid of the new process
          int status = 0, pid = 0;
!         VERBOSE(VB_CHANNEL, "Waiting for External Tuning program to exit");
  
          bool timed_out = false;
          uint timeout = 30; // how long to wait in seconds
--- 285,291 ----
      else
      {   // child contains the pid of the new process
          int status = 0, pid = 0;
!         VERBOSE(VB_CHANNEL, "Waiting for External Changer Deamon to finish starting");
  
          bool timed_out = false;
          uint timeout = 30; // how long to wait in seconds
***************
*** 212,240 ****
          }
          if (timed_out)
          {
!             VERBOSE(VB_IMPORTANT, "External Tuning program timed out, killing");
              kill(child, SIGTERM);
              usleep(500);
              kill(child, SIGKILL);
              return false;
          }
  
!         VERBOSE(VB_CHANNEL, "External Tuning program no longer running");
          if (WIFEXITED(status))
          {   // program exited normally
              int ret = WEXITSTATUS(status);
              if (ret)
              {   // external tuning program returned error value
                  VERBOSE(VB_IMPORTANT,
!                         QString("ChannelBase: external tuning program "
                                  "exited with error %1").arg(ret));
                  return false;
              }
!             VERBOSE(VB_IMPORTANT, "External Tuning program exited with no error");
          }
          else
          {   // program exited abnormally
!             QString msg = QString("ChannelBase: external tuning program "
                                    "encountered error %1 -- ").arg(errno);
              msg.append(strerror(errno));
              VERBOSE(VB_IMPORTANT, msg);
--- 303,331 ----
          }
          if (timed_out)
          {
!             VERBOSE(VB_IMPORTANT, "Deamon External Tuning program timed out, killing");
              kill(child, SIGTERM);
              usleep(500);
              kill(child, SIGKILL);
              return false;
          }
  
!         VERBOSE(VB_CHANNEL, "Deamon External Tuning program no longer running");
          if (WIFEXITED(status))
          {   // program exited normally
              int ret = WEXITSTATUS(status);
              if (ret)
              {   // external tuning program returned error value
                  VERBOSE(VB_IMPORTANT,
!                         QString("ChannelBase: deamon external tuning program "
                                  "exited with error %1").arg(ret));
                  return false;
              }
!             VERBOSE(VB_IMPORTANT, "Deamon External Tuning program exited with no error");
          }
          else
          {   // program exited abnormally
!             QString msg = QString("ChannelBase: deamon external tuning program "
                                    "encountered error %1 -- ").arg(errno);
              msg.append(strerror(errno));
              VERBOSE(VB_IMPORTANT, msg);


More information about the mythtv-dev mailing list