View unanswered posts    View active topics

All times are UTC - 6 hours





Post new topic Reply to topic  [ 4 posts ] 
Print view Previous topic   Next topic  
Author Message
Search for:
PostPosted: Wed Jan 18, 2006 3:07 am 
Offline
Joined: Mon Aug 29, 2005 12:54 pm
Posts: 6
I have a Palm Treo 600 with a 1 gig stick and a freebie movie player called Core Pocket Media Player or TCPMP.

http://corecodec.org/frs/?group_id=53&release_id=99

It can play Divx and Xvid videos. (which I have installed on my myth box)

http://mysettopbox.tv/phpBB2/viewtopic.php?t=4706


I also have a 3 yr who gets chaotic if he gets bored so I like to carry around something for him to watch on my Treo. Don't want to carry commercials though, the Treo heavy enough as is.

I setup a process to record a TV show, flag it for commercials, create a cutlist, run it through commercial_cut to ditch the corporatational blitz of night time TV, transcode it to an AVI 160x120 for use on the 600, then dump that into a sub-directory for me to copy to my memory stick.

A 1/2 hour TV show turns into a 19-21 minute, 15-20 meg file.

Ready?

Like any good *nix solution, we take something that works and bash it to death. Here is the bash file needed.

create /usr/local/sbin/treo_tranz

Code:
#!/bin/bash
echo "**********************************************************"
echo " "
echo "Transcoding to Treo"
echo "TRANSCODE TO TREO FORMAT IN: $1 OUT: /myth/tv/treo/$2.avi"
mencoder -srate 24000 -oac mp3lame -lameopts br=32:cbr:vol=6:mode=3 \
   -ovc lavc -lavcopts vcodec=mpeg4:vhq:vbitrate=64:keyint=300 \
   -vop scale=160:120 $1 -o "/myth/tv/treo/$2.avi"


make sure mythtv can run it! Crank up the scale=160x120 to suit your needs if you have a 650 or PPC.

Create the directory /myth/tv/treo and make sure mythtv can write/create there.

I use a modified version of Greg Frost and the gangs, commercial_cut. It's can't be used with RA26. It looks like this:

Code:
/* ===========================================================================
 * = NAME
 * commercial_cut.c
 *
 * = PURPOSE
 * Slice commercials out of a MythTV MPEG2 recording file.
 * The cuts are nowhere near perfect (i.e. frame accurate),
 * but it seems to work well for Australian DVB recordings
 * and has recently had code added for PVR-250/235 recordings
 * (which also seems to work for files inserted
 *  into MythTV from DVDs via mythcommflag --rebuild).
 *
 * = USAGE
 * commercial_cut -vvv /myth/tv/1005_20050614110000_20050614120000.nuv
 *   or
 * mv /usr/local/bin/mythtranscode /usr/local/bin/mythtranscode.orig
 * cp commercial_cut /usr/local/bin/mythtranscode
 * MythTV->Watch Recordings->{select a recording}->Begin Transcoding
 *
 * = DESCRIPTION
 * Gets the markups (frame number vs byte offset) and cutlist (frame ranges)
 * from the MythTV database, makes a copy of the original file without the
 * cutlist frames in it, adds a minute to the start time, and inserts it back
 * into the database. If there is a cut in the first 25 frames, or if the -k
 * flag is specified, the original file is kept, otherwise 'tis deleted.
 * Running as transcode has its own problems - transcode (or other jobs)
 * deletes entries from the recordedmarkup and recorded tables which
 * correspond to the original recording. In this case, we move the file
 * and table entries to a new start time (one minute before the original).
 *
 * = OPTIONS
 * Running the command without any options lists argument usage.
 * Arguments can also be specified in a .rc file, .commercial_cut.rc,
 * in your home directory.
 *
 * = REVISION
 * $Id: commercial_cut.c 1.6 2005/08/28 13:15:15 nigel Exp $
 * $Id: commercial_cut.c 1.6 2005/12/12 01:01:01 wp    -u (user job #4)
 *
 * = AUTHORS
 * Greg Frost, Nigel Pearson, Mudit Wahal
 * ========================================================================= */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <time.h>
#include <ctype.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>   /* For setpriority() */
#include <unistd.h>
#include <mysql/mysql.h>

/* This program is normally silent unless an error occurs.    */
/* To see what is happening, verbose output can be enabled.   */
#define output stdout
int verbose = 0;
/* There are three levels of verbosity:
/* The first outputs a few progress type messages.            */
/* The second is for SQL queries or some of the logical steps */
/* The third is for tedious stuff like each file block copied */
#define VERBOSE1(args...) if(verbose)   fprintf(output,args)
#define VERBOSE2(args...) if(verbose>1) fprintf(output,args)
#define VERBOSE3(args...) if(verbose>2) fprintf(output,args)

typedef struct
{
  int frame;
  off_t offset;
  int num_frames;
  off_t size;
} frame_mapping_type;

time_t get_time_t_from_filename_time (char *filename_time);


int mapping_comparison (const void *mm1, const void *mm2)
{
  frame_mapping_type *m1 = (frame_mapping_type *) mm1; 
  frame_mapping_type *m2 = (frame_mapping_type *) mm2; 

  if (m1->frame == m2->frame)
    return 0;
  if (m1->frame < m2->frame)
    return -1;
  else
    return 1;
}

time_t get_time_t_from_database_time (char *database_time)
{
  char time_string[100];
  struct tm time_tm = {0};
  time_t time_time_t;

  strncpy (time_string, database_time, sizeof (time_string));
    
  /* Parse the elements of the time string into a struct tm. */

  if (sscanf (database_time, "%d-%d-%d%*c%d:%d:%d",
         &time_tm.tm_year,
         &time_tm.tm_mon,
         &time_tm.tm_mday,
         &time_tm.tm_hour,
         &time_tm.tm_min,
         &time_tm.tm_sec) != 6)
  {
    fprintf (output, "Error parsing database time '%s'.\n", database_time);
    fprintf (output, "Trying time from filename\n");
    return get_time_t_from_filename_time(database_time);
  }

  time_tm.tm_year -= 1900;
  time_tm.tm_mon -= 1;
    
  time_time_t = mktime (&time_tm);

  VERBOSE1("database time %s is %s", database_time, ctime (&time_time_t));

  return time_time_t;
}

time_t get_time_t_from_filename_time (char *filename_time)
{
  struct tm time = {0};

  if (sscanf(filename_time, "%4d%2d%2d%2d%2d%2d",
             &time.tm_year, &time.tm_mon, &time.tm_mday,
             &time.tm_hour, &time.tm_min, &time.tm_sec) != 6)
  {
    fprintf(output, "Error parsing filename time '%s'.\n",filename_time);
    exit(0);
  }

  time.tm_year -= 1900;
  time.tm_mon  -= 1;

  return mktime (&time);
}

char *time_t_to_date_string (time_t time_time_t)
{
  char *date_string;
  struct tm time_tm;
  date_string = malloc (100);

  time_tm = *gmtime (&time_time_t);

  sprintf (date_string, "%04d%02d%02d%02d%02d%02d",
      time_tm.tm_year + 1900,
          time_tm.tm_mon + 1,
          time_tm.tm_mday,
          time_tm.tm_hour,
          time_tm.tm_min,
          time_tm.tm_sec);
  return date_string;
  // yeah its a memory leak, but its easy!
}

int main (int argc, char*argv[])
{
  char input_file_full_path[200];
  char output_file_full_path[200];
  char input_file_name[200];
  int count_loop = 0;
  int chanid = 0;
  int do_usleep = 0;
  char start_time[200];
  char end_time[200];
  time_t start_time_t = 0;
  time_t end_time_t;
  time_t new_start_time_t;
  time_t new_end_time_t;
  FILE *input_file;
  FILE *output_file;
  off_t output_file_size;
  off_t seek_to;
  off_t end_of_data;
  char buffer[65536];
  off_t num_to_write;
  int seek_arg;
  int cut_start[2000];
  int cut_end[2000];
  int num_cuts = 0;
  frame_mapping_type mapping[100000];
  frame_mapping_type new_mapping[100000];
  int num_mappings = 0;
  fpos_t file_pos;
  int new_map_index = 0;
  char *title = NULL;
  char *subtitle = NULL;
  char *description = NULL;
  int delete_original = 1;   /* By default, we delete the original   */
  int cutlist_for_delete = 0;   /* and check cutlist in first 25 frames */
  int running_as_transcode = 0;
  int experimental_pvr_x50_mode = 0;
  int num_fields;
  int   NumArgs = 0;      /* Fake argc and argv */
  char *ArgStrs[25];      /* Max of 25 args */
  char rcpath[100];
  FILE *rc;
  int run_job_4 = 0;

  MYSQL mysql;
  MYSQL_RES *res;
  MYSQL_ROW row;

  /* Make sure that mktime doesnt stuff up because of the timezone.*/

  putenv ("TZ=");

  fprintf (output, "Running NTSC version of Greg Frost's Crude Commercial Cutter\n");


  /* The following trickery lets us parse a .rc file with getopt() */

  sprintf(rcpath, "%s/.commercial_cut.rc", getenv("HOME"));
  rc = fopen(rcpath, "r");
  if ( rc )
  {
    char *s;
    int   len;

    fseek(rc, 0, SEEK_END);
    len = ftell(rc);
    fseek(rc, 0, SEEK_SET);
    /* fprintf(output, "Found %s of %d bytes\n", rcpath, len); */

    s = malloc(len);
    if ( !s )
    {
      fprintf(output, "Couldn't allocate %d bytes\n", len);
      exit(-1);
    }

    if ( fread(s, 1, len, rc) != len )
    {
      fprintf(output,
              "Couldn't read %d bytes from ~/.commercial_cut.rc\n", len);
      exit(-1);
    }

    /* Build up a fake set of arguments */

    ArgStrs[0] = argv[0];
    NumArgs = 1;
    for ( int RCarg = 1; RCarg < 10; ++RCarg )
    {
      ArgStrs[RCarg] = s;   /* Store start of string in fake arglist */
      ++NumArgs;

      /* Arguments are separated by either whitespace or EOF */

      while ( *s && !isspace(*s) )
   ++s;

      /* If we are terminated by whitespace, terminate this string */

      while ( *s && isspace(*s) )
   *s++ = '\0';

      if ( ! *s )   /* At end of data. No more arguments */
   break;
    }

    for ( int arg = 1; arg < argc; ++arg )
      ArgStrs[NumArgs++] = argv[arg];

    argc = NumArgs, argv = ArgStrs;
  }

  /* Process all args */

  while (1)
  {
    int optchar;

    optchar = getopt (argc, argv, "TSDcspdlknuvx");
   
    if (optchar == -1)
      break;
   
    switch (optchar)
    {
      case 'T':
   VERBOSE1("Title: %s\n", argv[optind]);
    title = argv[optind++];
   break;
       
      case 'S':
   VERBOSE1("Subtitle: %s\n", argv[optind]);
    subtitle = argv[optind++];
     break;
     
      case 'D':
   VERBOSE1("Description: %s\n", argv[optind]);
    description = argv[optind++];
    break;

      case 'c':
   VERBOSE1("Chanid: %s\n", argv[optind]);
    chanid = atoi (argv[optind]);
    optind++;
    break;

      case 's':
     strncpy (start_time, argv[optind], sizeof (start_time));
    optind++;
    start_time_t = get_time_t_from_database_time (start_time);
   VERBOSE1("start time is %s\n", ctime (&start_time_t));
    break;

      case 'p':
   VERBOSE1("Ignoring profile '%s' specification.\n", argv[optind]);
    optind++;
    break;

      case 'd':
   VERBOSE1("Storing status in db (which is stored regardless).\n");
   break;

      case 'k':
   VERBOSE1("Keeping the original recording.\n");
   delete_original = 0;
    break;

      case 'l':
   VERBOSE1("Using cutlist (which will be used regardless).\n");
    break;   

      case 'n':
   VERBOSE1("Run commercial_cut nicely, with usleep after writes.\n");
   /* Renice ourselves: */
        setpriority(PRIO_PROCESS, getpid(), 10);
   do_usleep = 1;
    break;

      case 'v':
   if (verbose)
          fprintf (output, "Verbosity increased.\n");
   else
     fprintf (output, "Verbose output enabled.\n");
         ++verbose;
    break;   

      case 'x':
   VERBOSE1("Forcing deletion of the original recording.\n");
   delete_original = 1;
   cutlist_for_delete = 0;
    break;
     
      case 'u':
   VERBOSE1("Queue user job 4 on new file when exiting.\n");
   run_job_4 = 1;
    break;
    }
  }

  //if (verbose > 1)
    for (int i = 0; i < argc; i ++)
      fprintf (output, "arg %d = '%s'\n", i, argv[i]);

  /* Does the command name match transcode? */
  if (strcmp(basename(argv[0]), "mythtranscode") == 0)
  {
    running_as_transcode = 1;
    VERBOSE1("Pretending to be mythtranscode!\n");
  }

  // Check that there is either still an unparsed option (the file name)
  // or else the start time and chanid have been specified explicitly.

  if ((optind >= argc) && !(start_time_t && chanid))
  {
    fprintf (output,
      "Usage:\n"
      "\n"
      "./commercial_cut [-T \"Title\"] [-S \"Subtitle\"] [-D \"Description\"]\n"
      "                 [-c chanid -s starttime] [-k] [-n] [-v] [-x] [input.nuv]\n"
      "\n"
      "-k    Keep the original file.\n"
      "-n    Run nicely, with usleep after some writes.\n"
      "-u    Insert user job #4 in job queue for the file that is created.\n"
      "-v    Verbose. Print extra output about commercial cut's actions.\n"
      "-x    Force delete of original file (despite cutlist).\n"
      "You must either pass a file that is a valid myth recording that can\n"
      "be found in the mythconverg database, or a chanid and starttime of\n"
      "such a recording (this is the format supplied to mythtranscode).\n");
    exit (1);
  }

  mysql_init (&mysql);
 
  if (!mysql_real_connect (&mysql, "mythbe", "mythtv", "mythtv",
              "mythconverg", 0, NULL, 0))
  {
    fprintf (output, "Can't connect to mythconverg MYSQL database\n");
    exit (1);
  }
  else
    VERBOSE2("Connected to the database successfully!\n");

  if (start_time_t && chanid)
  {
    /* Get the end time from the database by queryingthe recorded table. */
    VERBOSE2("Query for end time\n");
 
    //hack more to do.

    sprintf (buffer, "SELECT endtime FROM recorded WHERE chanid = %d AND "
                     "starttime = '%s'", chanid, start_time);
 
    VERBOSE2("%s\n", buffer);
 
    if (mysql_real_query (&mysql, buffer, strlen(buffer)))
    {
      fprintf (output, "Error querying database!\n");
      exit (1);
    }
    else
      VERBOSE2("Queried the database successfully!\n");   

    res = mysql_use_result (&mysql);
 
    if (!res)
    {
      fprintf (output, "Error using result\n");
      exit (1);
    }
    else
      VERBOSE2("Result with %d fields\n", mysql_field_count (&mysql));

    if (mysql_field_count (&mysql) != 1)
    {
      fprintf (output, "More than one recording returned???\n");
      exit (1);
    }
 
    if (mysql_num_fields (res) != 1)
    {
      fprintf (output, "More than one endtime field returned??\n");
      exit (1);
    }

    row = mysql_fetch_row (res);

    if (!row)
    {
      fprintf (output, "Error getting endtime\n");
      exit (1);
    }
    else
      VERBOSE2("Endtime is:\n%s", row[0]);

    end_time_t = get_time_t_from_database_time (row[0]);

    mysql_free_result (res);
  }
  else
  { 
    strcpy (input_file_full_path, argv[optind]);

    struct stat fs;
    if (stat(input_file_full_path, &fs) == -1)
    {
      VERBOSE1("File %s doesn't exist. Trying in /myth/tv\n", argv[optind]);
      sprintf(input_file_full_path, "/myth/tv/%s", argv[optind]);
      if (stat(input_file_full_path, &fs) == -1)
      {
        fprintf(output, "Error. File [/myth/tv/]%s doesn't exist.\n",
                argv[optind]);
        exit(-1);
      }
    }

    /* Extract the channel id and the start and end time from the name of */
    /* the recording nuv file. */

    strncpy (input_file_name, basename (input_file_full_path),
            sizeof (input_file_name));

    if (sscanf (input_file_name,
                "%d_%14s_%14s.nuv", &chanid, start_time, end_time) != 3)
    {
      fprintf (output, "Error getting channel-id_start-time_end-time\n");
      fprintf (output, "      from file name %s\n", input_file_name);
      exit (1);
    }

    /* Parse the start and end times into a time_t */
    start_time_t = get_time_t_from_filename_time(start_time);
    end_time_t   = get_time_t_from_filename_time(end_time);
  }
 
  VERBOSE1("The start time is %s", ctime (&start_time_t));
  VERBOSE1("The end   time is %s", ctime (&end_time_t));

  /* Attempt to adjust a transcode entry in the jobqueue to indicate that */
  /* The transcode is now running. */

  sprintf (buffer,
       "UPDATE jobqueue SET status=4 where chanid=%d and starttime=%s "
       "and type=1",
      chanid, time_t_to_date_string (start_time_t));
        
  VERBOSE2("%s\n", buffer);

  if (mysql_real_query (&mysql, buffer, strlen(buffer)))
  {
    fprintf (output, "Error altering jobqueue status to running!\n");
    exit (1);
  }
  else
    VERBOSE1("set jobqueue status to running\n");   

  sprintf (input_file_full_path, "/myth/tv/%d_%s_%s.nuv", chanid,
        time_t_to_date_string (start_time_t),
        time_t_to_date_string (end_time_t));

  sprintf (output_file_full_path, "/myth/tv/%d_%s_%s.nuv.tmp",
        chanid,
        time_t_to_date_string (start_time_t),
        time_t_to_date_string (end_time_t));

  VERBOSE1("Input File Full Path: %s\n"
           "Output File Full Path: %s\n",
           input_file_full_path, output_file_full_path);

  sprintf (buffer, "SELECT cutlist FROM recorded WHERE chanid = %d AND "
                   "starttime = '%s'", chanid, start_time);
 
  VERBOSE2("%s\n", buffer);
 
  if (mysql_real_query (&mysql, buffer, strlen(buffer)))
  {
    fprintf (output, "Error querying database!\n");
    exit (1);
  }
  else
    VERBOSE2("Queried the database successfully!\n");   

  res = mysql_use_result (&mysql);
 
  if (!res)
  {
    fprintf (output, "Error using result\n");
    exit (1);
  }
  else
    VERBOSE2("Result with %d fields\n", mysql_field_count (&mysql));

  if (mysql_field_count (&mysql) != 1)
  {
    fprintf (output, "More than one recording returned???\n");
    exit (1);
  }
 
  if (mysql_num_fields (res) != 1)
  {
    fprintf (output, "More than one cutlist field returned??\n");
    exit (1);
  }

  row = mysql_fetch_row (res);

  if (!row)
  {
    fprintf (output, "Error getting cutlist\n");
    exit (1);
  }
  else
    VERBOSE1("Cutlist is:\n%s", row[0]);

  /* Parse the cutlist to get the start and end sections. */

  {
    char *s = row[0];
    char *line;
    while (line = strtok (s, "\n"))
    {
      s = NULL;
      if (sscanf (line, "%d - %d", &cut_start[num_cuts],
              &cut_end[num_cuts]) != 2)
      {
        fprintf (output, "Error Parsing Cutlist\n");
   exit (1);
      }
      num_cuts++;
    }
  }

  /* If there is at least one cut, and the first cut is non-zero and */
  /* less than 25 (i.e a delete after right near the start of the */
  /* recording) then keep the original recording. */
 
  if (cutlist_for_delete &&
      (num_cuts > 0) && (cut_start[0] > 0) && (cut_start[0] < 25))
  {
    delete_original = 0;
    cut_start[0] = 0;
  }

  if (delete_original)
    VERBOSE1("Will delete the original recording\n");
  else
    VERBOSE1("Will keep the original recording\n");

  if (verbose)
    for (int cut = 0; cut < num_cuts; cut++)
      fprintf(output, "Cutlist %d: %d - %d\n",
              cut, cut_start[cut], cut_end[cut]);

  mysql_free_result (res);

  /* Now get the information from the recordedmarkup for the frame */
  /* file offset mappings. */

  sprintf (buffer, "SELECT mark,offset FROM recordedmarkup WHERE chanid = %d AND "
                   "starttime = '%s' AND type=9", chanid, start_time);
 
  VERBOSE2("%s\n", buffer);
 
  if (mysql_real_query (&mysql, buffer, strlen(buffer)))
  {
    fprintf (output, "Error querying database!\n");
    exit (1);
  }
  else
    VERBOSE2("Queried the database successfully!\n");   

  res = mysql_use_result (&mysql);
 
  if (!res)
  {
    fprintf (output, "Error using result\n");
    exit (1);
  }
  else
    VERBOSE2("Result with %d fields\n", mysql_field_count (&mysql));

  row = mysql_fetch_row (res);

  if (!row)
  {
    VERBOSE1("No type 9 entries in the database. Entering experimental PVR-x50 mode\n");
    experimental_pvr_x50_mode = 1;
    mysql_free_result (res);

    sprintf (buffer, "SELECT mark,offset FROM recordedmarkup WHERE chanid = %d AND "
                     "starttime = '%s' AND type=6", chanid, start_time);
 
    VERBOSE2("%s\n", buffer);
 
    if (mysql_real_query (&mysql, buffer, strlen(buffer)))
    {
      fprintf (output, "Error querying database!\n");
      exit (1);
    }
    else
      VERBOSE2("Queried the database successfully!\n");   

    res = mysql_use_result (&mysql);
 
    if (!res)
    {
      fprintf (output, "Error using result\n");
      exit (1);
    }
    else
      VERBOSE2("Result with %d fields\n", mysql_field_count (&mysql));

    row = mysql_fetch_row (res);

    if (!row)
    {
   fprintf (output, "No rows in recordedmarkup of type 6 or type 9. I'm, going to have to give up!\n");
   exit (1);
    }
  }

  do
  {
    VERBOSE3("%s %s\n", row[0], row[1]);
   
    if (sscanf (row[0], "%d", &mapping[num_mappings].frame) != 1)
    {
      fprintf (output, "error parsing frame from frame mapping %d\n",
               num_mappings);
      exit (1);
    }
     
    if (sscanf (row[1], "%lld", &mapping[num_mappings].offset) != 1)
    {
      fprintf (output, "error parsing offset from frame mapping %d\n",
               num_mappings);
      exit (1);
    }
     
    num_mappings++;
  } while (row = mysql_fetch_row (res));

  mysql_free_result (res);

  if (experimental_pvr_x50_mode)
  {
    for (int map = 0; map < num_mappings; map++)
   mapping[map].frame *= 15;
  }

  qsort (mapping, num_mappings, sizeof (mapping[0]), mapping_comparison);

  /* Determine the number of frames and size of each mapping. */

  for (int map = 0; map < num_mappings - 1; map++)
  {
    mapping[map].num_frames =  mapping[map + 1].frame - mapping[map].frame,
    mapping[map].size = mapping[map + 1].offset - mapping[map].offset;
  }

  /* Determine the size of the last mapping using the file size. */

  mapping[num_mappings - 1].num_frames = 0;
  {
    struct stat file_stats;
    int stat_return;
    if (stat (input_file_full_path, &file_stats))
    {
      fprintf (output, "Cannot stat file %s\n", input_file_full_path);
      exit (1);
    }
    mapping[num_mappings - 1].size = file_stats.st_size;
  }
 
  VERBOSE1("file size is %lld\n", mapping[num_mappings - 1].size);
  mapping[num_mappings - 1].size =
    mapping[num_mappings - 1].size - mapping[num_mappings - 1].offset;

  if (verbose>2)
    for (int map = 0; map < num_mappings; map++)
    {
      fprintf (output, "mapping %d: %d %lld size %d %lld\n",
               map,
               mapping[map].frame,
               mapping[map].offset,
               mapping[map].num_frames,
               mapping[map].size);
    }

  /* Now, copy one file to the next whilst cutting out the commercials */
  /* and producing a set of mappings.                                  */

  input_file = fopen (input_file_full_path, "r");
  if (!input_file)
  {
    fprintf (output, "Error opening file %s\n", input_file_full_path);
    exit (1);
  }

  output_file = fopen (output_file_full_path, "w");
  if (!output_file)
  {
    fprintf (output, "Error opening output file %s\n", output_file_full_path);
    exit (1);
  }

  /* The list of type 6 mappings I was given to examine had the */
  /* offset for frame zero at 38. I don't know what these first */
  /* 38 bytes are, but I'll copy them to the new file anyway. */

  /* We need to read up to the first offset otherwise all of */
  /* the seeks will be wrong! It should not do any harm to do */
  /* this for DVB fince the first offset should be zero. */

  {
     int num_bytes_to_copy = mapping[0].offset;
     count_loop = 0;

   while (num_bytes_to_copy != 0)
        {
          int num_this_time;
   
          if (num_bytes_to_copy < sizeof (buffer))
            num_this_time = num_bytes_to_copy;
          else
            num_this_time = sizeof (buffer);
     
          fread (buffer, num_this_time, 1, input_file);
          fwrite (buffer, num_this_time, 1, output_file);
          num_bytes_to_copy -= num_this_time;

          if (do_usleep && count_loop++ % 5 == 0)
            usleep(1000);
        }
     VERBOSE2("loops in first count = %d\n", count_loop);
  }

  {
    int map_index = 0;
    off_t num_bytes_to_copy;

    /* For each cut, copy up to the start of the cut and then seek past */
    /* the cut portion */
    for (int cut = 0; cut <= num_cuts; cut++)
    {
      num_bytes_to_copy = 0;
     
      while (map_index < num_mappings &&
             ((cut == num_cuts) ||
              ((mapping[map_index].frame + mapping[map_index].num_frames) <
               cut_start[cut])))
      {
        new_mapping[new_map_index].num_frames = mapping[map_index].num_frames;
        new_mapping[new_map_index].size = mapping[map_index].size;

         num_bytes_to_copy = mapping[map_index].size;
     
        VERBOSE3("Copying %lld bytes from %lld\n",
                 num_bytes_to_copy, mapping[map_index].offset);

   while (num_bytes_to_copy != 0)
        {
          int num_this_time;
   
          if (num_bytes_to_copy < sizeof (buffer))
            num_this_time = num_bytes_to_copy;
          else
            num_this_time = sizeof (buffer);
     
          fread (buffer, num_this_time, 1, input_file);
          fwrite (buffer, num_this_time, 1, output_file);
          num_bytes_to_copy -= num_this_time;
        }

   map_index++;
   new_map_index++;
      }
     

      /* Now skip data until we have passed the cut point. */

      while (map_index < num_mappings &&
             ((cut == num_cuts) || (mapping[map_index].frame <= cut_end[cut])))
      {
   /* Skip the data we do not wish to copy. */

   VERBOSE3("skipping %d: %d frames %lld bytes",
                 map_index,
                 mapping[map_index].num_frames,
                 mapping[map_index].size);
   VERBOSE3(" from frame %d offset %lld\n",
                 mapping[map_index].frame,
                 mapping[map_index].offset);

   if (fseek (input_file, mapping[map_index].size, SEEK_CUR))
   {
     fprintf (output, "Error seeking input file ahead %lld\n",
         mapping[map_index].size);
     exit (1);
   }

   map_index++;
      }     
    }     
  }

  fclose (input_file);
  fclose (output_file);

  VERBOSE2("closed input and output files\n");

  /* Determine the size of the output file. */
 
  {
    struct stat file_stats;
    if (stat (output_file_full_path, &file_stats))
    {
      fprintf (output, "Cannot stat file %s\n", output_file_full_path);
      exit (1);
    }
    output_file_size = file_stats.st_size;
  }

  VERBOSE1("Output file size %lld\n", output_file_size);

  /* Calculate a new start time so that we can enter the new */
  /* mapping info into the database (without clashing with the */
  /* existing cut info. */

  new_start_time_t = start_time_t;

  do
  {
    if (new_start_time_t != start_time_t)
      VERBOSE1("start time %s already in database!???\n",
               time_t_to_date_string (new_start_time_t));

    new_start_time_t = new_start_time_t + 60;

    sprintf (buffer,
           "SELECT * FROM recorded WHERE chanid = %d AND starttime = '%s'",
           chanid, time_t_to_date_string (new_start_time_t));

    VERBOSE2("%s\n", buffer);
         
    if (mysql_real_query (&mysql, buffer, strlen(buffer)) != 0)
    {
      fprintf (output, "Error querying for existing entry\n");
      exit (1);
    }

    res = mysql_use_result (&mysql); 
    row = mysql_fetch_row (res);
    mysql_free_result (res);
 
    if (!row)
      break;
  } while (1);
 
  VERBOSE1("new start time %s", ctime (&new_start_time_t));

  /* Now calculate the new recordedmarkup info. */
 
  for (int map_index = 0; map_index < new_map_index; map_index++)
  {
    if (map_index == 0)
    {
      new_mapping[map_index].frame = 0;
      new_mapping[map_index].offset = 0;
    }
    else
    {
      new_mapping[map_index].frame =
        new_mapping[map_index - 1].frame +
        new_mapping[map_index - 1].num_frames;
      new_mapping[map_index].offset =
        new_mapping[map_index - 1].offset +
        new_mapping[map_index - 1].size;
    }
  }

  if (experimental_pvr_x50_mode)
  {
    for (int map_index = 0; map_index < new_map_index; map_index++)
      new_mapping[map_index].frame /= 15;
  }

  /* Now insert the new recordedmarkup information into the database. */
 
  for (int map_index = 0; map_index < new_map_index; map_index++)
  {
    sprintf (buffer,
            "INSERT INTO recordedmarkup VALUES (%d,'%s',%d,'%lld',%d)",
        chanid, time_t_to_date_string (new_start_time_t),
        new_mapping[map_index].frame,
        new_mapping[map_index].offset,
        experimental_pvr_x50_mode ? 6 : 9);
        
    VERBOSE3("new map %d: %s", map_index, buffer);

    if (mysql_real_query (&mysql, buffer, strlen(buffer)))
    {
      fprintf (output, "Error entering recordedmarkup into database!\n");
      exit (1);
    }
    else
      VERBOSE3("Entered recordedmarkup into db successfully!\n");   
  }
 
  /* Create a new end time based on the number of frames in */
  /* the cut recording. */

  new_end_time_t =
    new_start_time_t + new_mapping[new_map_index - 1].frame *
    (experimental_pvr_x50_mode ? 15 : 1) / 25 / 60 * 60 + 60;
 
  VERBOSE1("new end time %s", ctime (&new_end_time_t));

  /* Get all of the info from the original recording so we can */
  /* write a new entry to the database. */

  sprintf (buffer, "SELECT * FROM recorded WHERE chanid = %d AND "
                   "starttime = '%s'", chanid, start_time);
  VERBOSE2("%s\n", buffer);
 
  if (mysql_real_query (&mysql, buffer, strlen(buffer)))
  {
    fprintf (output, "Error querying database!\n");
    exit (1);
  }
  else
  {
    VERBOSE2("Queried the database successfully!\n");   
  }

  res = mysql_use_result (&mysql);
 
  if (!res)
  {
    fprintf (output, "Error using result[A\n");
    exit (1);
  }
  else
  {
    num_fields = mysql_field_count (&mysql);
    VERBOSE2("Result with %d fields\n", num_fields);
  }

  if (num_fields != 25)
  {
    fprintf (output, "Expected 25 fields in recorded got %d\n", num_fields);
    // 0.16 had 22 fields (up to originalairdate)
    // 0.17 and 0.18 have 25 fields (up to deletepending)
    fprintf (output, "DB newer than 0.18. This may cause problems?\n");
  }

  row = mysql_fetch_row (res);

  {
    char *escaped_title;
    char *escaped_subtitle;
    char *escaped_description;
 
    if (!title)
      title = row[3];
    if (!subtitle)
      subtitle = row[4];
    if (!description)
      description = row[5];

    escaped_title = calloc (strlen (title) * 3, 1);
    escaped_subtitle = calloc (strlen (subtitle) * 3, 1);
    escaped_description = calloc (strlen (description) * 3, 1);

    mysql_real_escape_string (
      &mysql, escaped_title, title, strlen (title));
    mysql_real_escape_string (
      &mysql, escaped_subtitle, subtitle, strlen (subtitle));
    mysql_real_escape_string (
      &mysql, escaped_description, description, strlen (description));

    sprintf (buffer,"INSERT INTO recorded VALUES (%s,'%s','%s','%s','%s','%s',",
             row[0],       // chanid
             time_t_to_date_string (new_start_time_t),
             time_t_to_date_string (new_end_time_t),
             escaped_title,
             escaped_subtitle,
             escaped_description);

    sprintf (buffer+strlen(buffer), "'%s','%s',NULL,%s,'',%s,%s,'%s',",
             row[6],      // category
             row[7],      // hostname
             row[9],      // editing
             row[11],      // autoexpire
             row[12],      // commflagged
             row[13]);      // recgroup

    if (!row[14])
      row[14] = "0";

    sprintf (buffer+strlen(buffer), "%d,'%s','%s',%s,%lld,%s,%s",
             atoi(row[14]) + 1,   // recordid
             row[15],      // seriesid
             row[16],      // programid
             row[17],      // lastmodified
             output_file_size,
             row[19],      // stars
             row[20]);      // previouslyshown

    for (int field = 21; field < num_fields; ++field)
    {
      VERBOSE2("Adding field %d - %s\n", field, row[field]);
      sprintf (buffer+strlen(buffer), ",'%s'", row[field]);
    }

    strcat(buffer+strlen(buffer), ")");
  }

  mysql_free_result (res);
     
  VERBOSE2("%s\n", buffer);

  if (mysql_real_query (&mysql, buffer, strlen(buffer)))
  {
    fprintf (output, "Error querying database!\n");
    exit (1);
  }
  else
    VERBOSE2("Queried the database successfully!\n");   

  /* Rename the temporary file so that it matches the new start and end times. */

  {
    char new_output_name[200];
    sprintf (new_output_name, "/myth/tv/%d_%s_%s.nuv", chanid,
        time_t_to_date_string (new_start_time_t),
        time_t_to_date_string (new_end_time_t));
    if (rename (output_file_full_path, new_output_name))
    {
      fprintf (output, "error renaming %s to %s\n",
                output_file_full_path, new_output_name);
      exit (1);
    }
  }

  if (delete_original)
  {
    /* Remove the original recording from the database and the file */
    VERBOSE1("deleting original\n");

    if (unlink (input_file_full_path))
      fprintf (output, "error deleting %s\n", input_file_full_path);
    else
      VERBOSE1("deleted %s\n", input_file_full_path);

    sprintf (buffer,
            "DELETE FROM recorded where chanid=%d and starttime=%s",
        chanid, time_t_to_date_string (start_time_t));
        
    VERBOSE2("%s\n", buffer);

    if (mysql_real_query (&mysql, buffer, strlen(buffer)))
    {
      fprintf (output, "Error deleting original recorded!\n");
      exit (1);
    }
    else
      VERBOSE2("Deleted original recorded successfully\n");   

    sprintf (buffer,
            "DELETE FROM recordedmarkup where chanid=%d and starttime=%s",
        chanid, time_t_to_date_string (start_time_t));
        
    VERBOSE2("%s\n", buffer);

    if (mysql_real_query (&mysql, buffer, strlen(buffer)))
    {
      fprintf (output, "Error deleting original recordedmarkup!\n");
      exit (1);
    }
    else
      VERBOSE2("Deleted original recordedmarkup successfully\n");   
  }

  if (running_as_transcode && !delete_original)
  {
    VERBOSE1("attempting to do what is needed to keep original\n");

    /* Find a new unique time for the recording in the past. */
   
    /* This needs to be done because for some reason, myth deletes the */
    /* recordedmarkup and the recording when the transcode job completes. */
       
    new_start_time_t = start_time_t;

    do
    {
      if (new_start_time_t != start_time_t)
        fprintf (output, "start time %s already in database!\n",
                 time_t_to_date_string (new_start_time_t));

      new_start_time_t = new_start_time_t - 60;

      sprintf (buffer,
             "SELECT * FROM recorded WHERE chanid = %d AND starttime = '%s'",
             chanid, time_t_to_date_string (new_start_time_t));

      VERBOSE2("%s\n", buffer);
         
      if (mysql_real_query (&mysql, buffer, strlen(buffer)) != 0)
      {
        fprintf (output, "Error querying for existing entry\n");
        exit (1);
      }

      res = mysql_use_result (&mysql); 
      row = mysql_fetch_row (res);
      mysql_free_result (res);
 
      if (!row)
        break;
    } while (1);
 
    VERBOSE1("new start time %s", ctime (&new_start_time_t));

    sprintf (output_file_full_path, "/myth/tv/%d_%s_%s.nuv",
            chanid, time_t_to_date_string (new_start_time_t),
            time_t_to_date_string (end_time_t));

    if (rename (input_file_full_path, output_file_full_path))
    {
      fprintf (output, "error renaming %s to %s\n",
                input_file_full_path, output_file_full_path);
      exit (1);
    }
    else
      VERBOSE1("renamed %s to %s\n",
               input_file_full_path, output_file_full_path);

    sprintf (
      buffer,
      "UPDATE recorded SET starttime=%s where chanid=%d and starttime=%s",
      time_t_to_date_string (new_start_time_t),
      chanid, time_t_to_date_string (start_time_t));
        
    VERBOSE2("%s\n", buffer);

    if (mysql_real_query (&mysql, buffer, strlen(buffer)))
    {
      fprintf (output, "Error changing the recording start time!\n");
      exit (1);
    }
    else
      VERBOSE2("changed the recording start time.\n");   

    sprintf (
      buffer,
      "UPDATE recordedmarkup SET starttime=%s where chanid=%d and starttime=%s",
      time_t_to_date_string (new_start_time_t),
      chanid, time_t_to_date_string (start_time_t));
        
    VERBOSE2("%s\n", buffer);

    if (mysql_real_query (&mysql, buffer, strlen(buffer)))
    {
      fprintf (output, "Error changing the recordedmarkup start time!\n");
      exit (1);
    }
    else
      VERBOSE2("changed the recordedmarkup start time.\n");   
  }

  /* Update the jobqueue to indicate the job is now complete. */
     
  sprintf (buffer,
       "UPDATE jobqueue SET status=5 where chanid=%d and starttime=%s "
       "and type=1",
      chanid, time_t_to_date_string (start_time_t));
        
  VERBOSE2("%s\n", buffer);

  if (mysql_real_query (&mysql, buffer, strlen(buffer)))
 {
    fprintf (output, "Error setting jobqueue status to stopped!\n");
    exit (1);
  }
  else
    VERBOSE1("jobqueue set to stopped.\n");   

/* WPMyers 12/4/2005 command line arg addtion for user job queue entry on exit */
/* Let's do a user job entry on the file that just got spit out. */

if (run_job_4) {
    sprintf (buffer,
            "INSERT INTO jobqueue (chanid, starttime, inserttime, type, status)  VALUES (%d,'%s',NOW(),2048,1)",
        chanid,
             time_t_to_date_string (new_start_time_t));
        
    VERBOSE1("Attempting to insert entry into the Job Queue\n");

    VERBOSE2("%s\n", buffer);

    if (mysql_real_query (&mysql, buffer, strlen(buffer)))
    {
      fprintf (output, "Error entering job into database queue!\n");
      exit (1);
    }
    else
      VERBOSE1("User Job entry successfull!\n");   
 }
 
  exit (0);
}




This version of commercial_cut has a -u option. It will load a user job #4 with it's output file for further processing.

Now setup some user jobs in mythtvsetup (make sure you hit NO to NOT NOODLE with your cards and channels when entering setup!!)

Image

Image

(use the same path to treo_tranz for job #4 that you used above)

/usr/local/bin/treo_tranz

(all the DIR%%FILE% stuff is the same on every line, portions cut for clarity)

Should do the trick. Hit FINISH and get out of there without screwing up anything else.

Meanwhile, back in mythfrontend, setup a TV schedule to record a job that looks like this:

Image

That's right America, I said DO NOT run job #4 on the originating file. Commercial_cut creates a new entry in the database that is not the same as the name of the file that started out "post processing". But you DO need to setup a user job #4 with the above mentioned script to be available once the modified version of comm_cut is done and enters a user job with the new name. That's what started all this.

If you have more than 1 backend you'll need treo_tranz and commercial_cut on all backends that might run the user job.

Caveats: I have a PVR 500, NTSC. My commercial_cut is for that platform and I needed to make those changes myself as outline elsewhere in this forum. If you've modified your comm_cut too, just diff it, I only added a few lines to get the job inserted into the SQL queue.

commercial_cut http://mysettopbox.tv/phpBB2/viewtopic.php?t=4318&highlight=commercialcut

SEE ALSO: http://mysettopbox.tv/phpBB2/viewtopic.php?t=7271&highlight=commercialcut

Your knoppmyth version: (well mine really)
# more /etc/KnoppMyth-version
R5 Alpha 22

Okay, hopefully you can sit back and enjoy your /myth/tv/treo directory as it fills up with commercial free TV shows for your Treo. Just copy them there however you like.

Hope this helps. If it doesn't work you are entitled to a full refund. The lifespan of commercial_cut seems to be sun-setting as the next version of mythcommflag has these features built-in. Until then, and until there is an upgrade path for me the get there, this works.

Bill


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 18, 2006 11:51 am 
Offline
Joined: Fri Sep 19, 2003 7:05 pm
Posts: 5088
Location: Fontana, Ca
Great writeup!

_________________
cesman

When the source is open, the possibilities are endless!


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 19, 2006 3:24 pm 
Offline
Joined: Sun Dec 04, 2005 1:44 pm
Posts: 403
Location: Central NJ
Very nice....you said it won't run on R5A26...will it run on R5A30.1? What exactly is the limiting factor?

_________________
Currently running: R5.5, HD5000 x 2, PVR150, Athlon 64 3000+, Chaintech VNF4, 1GB RAM, 2 x 250GB in LVM, MSI NX6200TC -> AA 9A60 -> HDTV


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 20, 2006 8:58 am 
Offline
Joined: Mon Aug 29, 2005 12:54 pm
Posts: 6
Thanks cesman. Always nice to give a little back to the community.


As far as commercial_cut and later versions of Myth, I'm not sure what the problem is. The link above talks about 1 guy having a problem with it going into la-la land while running it.


Bill


Top
 Profile  
 

Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 


All times are UTC - 6 hours




Who is online

Users browsing this forum: No registered users and 6 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group

Theme Created By ceyhansuyu