Log in

View Full Version : CCASDI Timing error


videophool
27th August 2013, 07:38
I ported timecodeof method from CCASDI.pl to Java, and am running some tests. I note that the converted timecode runs backwards by 1 frame at 10-minute boundaries as shown below. I did this test because I was using another person's ported version of the McPoodle conversion code, and it had the same issue.

plFrameToTimecode(71856): 00:40:00;02
plFrameToTimecode(71857): 00:40:00;01
plFrameToTimecode(71858): 00:40:00;02


private static void plFrameToTimeCode(int frames, boolean drop)
{
double frameRate = 29.97;
// hours
double divisor = 3600.0 * frameRate;
if (drop) {
divisor -= 108; // number of frames dropped every hour
}
int hh = (int)(frames / divisor);
double remainder = frames - (hh * divisor);
// tens of minutes (required by drop-frame)
divisor = 600 * frameRate;
if (drop) {
divisor -= 18; // number of frames dropped every 10 minutes
}
int dm = (int)(remainder / divisor);
remainder = remainder - (dm * divisor);
// single minutes
divisor = 60 * frameRate;
if (drop) {
divisor -= 2; // number of frames dropped every minute except the 10th
}
int sm = (int)(remainder / divisor);
int mm = dm * 10 + sm;
remainder = remainder - (sm * divisor);
// seconds
int ss = (int)(remainder / frameRate);
// frames
remainder -= (ss * frameRate);
int ff = (int) (remainder + 0.5);

// correct for calculation errors that would produce illegal timecodes
if (ff > frameRate) { ff = 0; ss++;}
// drop base means that first two frames of 9 out of 10 minutes don't exist
// i.e. 00:10:00;01 is legal but 00:11:00;01 is not
if ((drop) && (ff < 2) && (sm > 0)) {
frames = 2;
}
if (ss > 59) { ss = 0; mm++; }
if (mm > 59) { mm = 0; hh++; }

String frameDivider = ":";
if (drop) {
frameDivider = ";";
}
Tracer.traceString("plFrameToTimecode(%d): %02d:%02d:%02d%s%02d", frames, hh, mm, ss, frameDivider, ff);
}

raffriff42
27th August 2013, 13:44
I dunno, but I don't like the look of all that floating-point math...
Here are some timecode routines written 20 yrs. ago by a timecode pioneer, Jack Calaway (http://en.wikipedia.org/wiki/Linear_video_editing#Peak_usage).
Originally posted as TC.C on Compuserve (http://en.wikipedia.org/wiki/Compuserve) by Mr. Calaway himself, I believe.
See ltos() below for a function very close to your plFrameToTimeCode()
Note the decimal (digit-wise) math, with drop fame corrections applied as needed./*
These two routines convert ASCII SMPTE time code to binary, and from binary
back to a formatted ASCII string.

I normally store the binary value in a structure which is defined as:

struct t_buf
{
unsigned long int tim;
unsigned int tct;
};

Stol, the string to binary routine, skips junk characters, so the string can
have ':' or ';' for readability (not required). You may want to skip these
chars, but have the routine end if any other non-numeric character is in the
string (this is what my assembly language routines do).

Also, these routines needs to have the time code type 0 = ndf, 1 = df, any
other positive value for pal. I leave it to you to figure out the details on
this, depending on the application, this could be a constant, user entered,
or, as when reading an edl, it may be the character separating units of
seconds and tens of frames.

These routines don't know about 24 hrs. If you do addition or subtraction
with the binary values, you need to be sure that you correct for times less
than 0, or greater than 24 hours.

This test program was tested with MSC version 6.00.

By: J L Calaway
Software Systems / Sierra Madre
PO Box 111
Sierra Madre, Ca 91024

CIS 70047,1061

*/

/* prototypes */
unsigned long stol(char *, int);
void ltos(unsigned long *, char *, int);

#define ndf 0
#define df 1
#define pal 2

/* test code, convert string to binary, back to string & display */
main()
{
unsigned long test;
char src[] = "00:01:00:00"; /* invalid df value */
char reslt[] = " ";

/* first convert as ndf */
test = stol(src,ndf);
ltos(&test,reslt,ndf);
printf("\nndf = %s\n",reslt);

/* now as frames, greater than 29, up to 99 */
test = stol("0:82",ndf);
ltos(&test,reslt,ndf);
printf("frm = %s\n",reslt);

/* on both of these examples, notice format char */
/* now as pal */
test = stol(src,pal);
ltos(&test,reslt,pal);
printf("pal = %s\n",reslt);

/* now as df with an invalid time, notice correction */
test = stol(src,df);
ltos(&test,reslt,df);
printf("df = %s",reslt);
printf(", only a factor when an invalid time is entered\n");

/* convert then add 1, correct result */
test = stol("5929",df);
test++;
ltos(&test,reslt,df);
printf("df = %s\n",reslt);

/* now with a valid df time */
test = stol("10002",df);
ltos(&test,reslt,df);
printf("df = %s\n",reslt);

/* and finally, show why we have df */
/* convert 1 hr. ndf to bin, display as real time, df */
test = stol("01:00:00:00",ndf); /* non-drop value */
ltos(&test,reslt,df); /* show real value */
printf("%%# = %s",reslt);
printf(", Now I see why they keep cutting the end of my show!\n");

/* max values */
test = stol("24:00:00:00",ndf);
printf("ndf = %7.7lxh = 24hrs\n",test);

test = stol("24000000",df);
printf("df = %7.7lxh = 24hrs\n",test);

}

/******************************************************************************
*
* Time Code mathematics, formatting and conversion
*
******************************************************************************/

/******************************************************************************
* binary constants
******************************************************************************/

unsigned long frmtbl[] =
{
/* 0 +00 ndf (30 frames/sec) */
0x107ac0,0x01a5e0, /* 1,080,000, 108,000 */
0x004650,0x000708, /* 18,000, 1,800 */
0x00012c,0x00001e, /* 300, 30 */
0x00000a,0x000001, /* 10, 1 */

/* 1 +08 df (29.97 frames/sec) */
0x107688,0x01a574, /* 1,078,920, 107,892 */
0x00463e,0x000706, /* 17,982, 1,798 */
0x00012c,0x00001e, /* 300, 30 */
0x00000a,0x000001, /* 10, 1 */

/* 2 +16 pal (25 frames/second) */
0x0dbba0,0x015f90, /* 900,000, 90,000 */
0x003a98,0x0005dc, /* 15,000, 1,500 */
0x0000fa,0x000019, /* 250, 25 */
0x00000a,0x000001, /* 10, 1 */
};

/******************************************************************************
* convert smpte tc to long
******************************************************************************/

unsigned long stol(char * tc, int tct)
{
int l, c1;
unsigned long bin=0; /* result, default=0 */
unsigned long *t=&frmtbl[7]; /* base = units frames */

if(tct < 2) t+=(8 * tct); /* 0 = ndf, 1 = df */
else t+=(16); /* pal */

l=strlen(tc); /* max chars to convert */
tc+=l; /* to end of string */
c1=8; /* max table size */

/* work it out */
while(l >= 0 && c1 >=0) {
if(*tc >= '0' && *tc <= '9') { /* number */
bin+= (*(tc) - '0') * (*t--);
c1--;
l--;
tc--;
}
else { /* junk character */
l--;
tc--;
}
}
return(bin);
}

/******************************************************************************
* convert long to smpte tc
******************************************************************************/

void ltos(unsigned long * bin, char * dest, int tct)
{
register unsigned long int s;
register unsigned long int *t = frmtbl; /* table base */
register int c1;
register char *d = dest;
char c;

s=*bin; /* source pointer */

if(tct < 2) t+=(8 * tct); /* 0 = ndf, 1 = df */
else t+=(16); /* pal */

*(dest+2) = ':'; /* always colon */
*(dest+5) = ':'; /* always colon */
*(dest+8) = ':'; /* assume ndf */
*(dest+11) = 0; /* end it all */

/* drop frame? */
if(tct == 1) *(dest+8) = ';'; /* drop frame */
if(tct >= 2) *(dest+8) = '.'; /* pal */

/* do binary to smpte conversion */
for(c1=0; c1<4; c1++) {
/* msd */
'c = s / (*t); /* bin/10's table */
*d++ = c + '0'; /* make '0' - '9' */
s-= (c * (*t++)); /* bin - x * 10's table */

/* lsd */
c = s / (*t); /* bin/ 1's table */
*d++ = c + '0';
s-= (c * (*t++));

d++; /* skip over ':' */
}

/* df? */
if(tct != 1) return; /* only do df */

d=dest; /* smpte pointer */

/* check for df error */ ' "01:34:67:90"
if(*(d+4) == '0') return; ' is @ even ten-minutes; OK
if(*(d+6) != '0') return; ' proc if "##:##:00:0" & 2 to 9
if(*(d+7) != '0') return;
if(*(d+9) != '0') return;
if(*(d+10) >= '2') return;

/* fix drop frame error */
*(d+10) += 8; /* units frames */
*(d+9) = '2'; /* tens of frames */
*(d+7) = '9'; /* units of seconds */
*(d+6) = '5'; /* tens of frames */
*(d+4) -= 1; /* units of minutes */
}

videophool
5th September 2013, 04:22
I tested this against McPoodle. As far as I can tell, the McPoodle drop-frame calculations are off by 3 frames every 10 minutes. Appears to be causing slight drift, but I did not test fully.