//////////////////////////////////////////////////////////////////////

// SMPTETimecode.cpp

//////////////////////////////////////////////////////////////////////

// Demonstration of SMPTE Timecode Conversions

//

// Brooks Harris

// 2015-04-04

// http://www.edlmax.com/services.htm

//

// If this code works, I wrote it. If not, I've never heard of it.

//////////////////////////////////////////////////////////////////////

 

#ifdef SMPTE_TIMECODE_INTRODUCTION

/*///////////////////////////////////////////////////////////

// Conversion between SMPTE hh:mm:ss:ff Time Code and Frames

/////////////////////////////////////////////////////////////

 

Brooks Harris, EdlMax, LLC

Version V4 2014-07-26

 

SMPTE time code (sometimes called "SMPTE/EBU time code",

"EBU time code", or simply "timecode") is specified in the

SMPTE standard SMPTE ST 12-1 Time and Control Code, 

(formerly called "SMPTE 12M"). SMPTE ST 12-1 describes the

structure of time code data and its numbering schemes

together with digital encoding formats. SMTPE ST 12-1 is

the foundational document for "SMPTE time code" from which

many other time code-related SMPTE standards are derived.

 

The behavior of the time and frames data in SMPTE time code

derives from its original purpose for monitoring tape

position by frames. When it was invented the best way to

encode the position of a frame on tape was in the form of

hours:minutes:seconds:frames (hh:mm:ss:ff). The binary form

used in ST 12-1 is encoded using Binary Coded Decimal (BCD)

together with other operational metadata to enable real-

time signal detection of values, direction, and speed on

tape.

 

SMPTE time code can be thought of as a "position", or

"index"; an absolute integer number of frames from a zero

starting point encoded as an hh:mm:ss:ff number from a

00:00:00:00 "origin". This encoding form also has the

advantage of being human readable as a time.

 

SMPTE time code can also be represented in character (text)

format. The official form of character representation is

specified in SMPTE ST 258, Transfer of Edit Decision Lists.

The data elements of the text format follow the numbering

and counting schemes defined in SMPTE ST-12-1.

 

Both binary and text forms encode the frame position value

as hh:mm:ss:ff. Conversion between them is the subject of

this article.

 

--------------

Binary Formats

--------------

 

SMPTE ST-12-1 details information about three related

topics:

 

1) The time and frames data itself and its counting

methods. This aspect is often of most interest and

relevance to many software implementations.

 

2) Specification of how the binary versions of time code

are to be phase-aligned to the video signal they are

applied to.

 

3) The "transport layer", that is, the bit layout of time

code binary data and operational metadata. Two transports

are defined in SMPTE ST 12-1 - "Linear Time code (LTC)" is

used for magnetic tape position tracking and transporting

time code over (analog) wires, and Vertical Interval Time

code (VITC)" is used to transport time code data in the

vertical interval of analog video signals.  One additional

transport layer, defined in a companion document SMPTE ST

12-2 - "Ancillary Time Code (ATC)", is used to transport

time code data in digital video signals.

 

--------------

Text Formats

--------------

 

Time code can also be represented as text, the familiar

form seen on many system displays, in edit decision lists,

application parameter settings, etc. SMPTE ST 258, section

8 Time code specifies text-formatting rules for time code

character representation:

 

[hour]":"[minute]":"[second][":" | ";" | "." | ","][frames]

 

where the last separator (the character between seconds and

frames) indicates the count mode (Non-drop Frame or Drop

Frame) and "field 1" or "field 2" of interlace video.

 

"00:00:00.00" (period - field 1 non-drop-frame)

"00:00:00:00" (colon - field 2 non-drop-frame)

"00:00:00,00" (comma - field 1 drop-frame)

"00:00:00;00" (semicolon - field 2 drop-frame)

 

SMPTE Time code and text formatting does not explicitly

address "progressive" video formats. In this case it is

convention to default to ":" (non-drop-frame) or ";" (drop-

frame).

 

In systems and applications supporting conventional

interlace video formats the "field 1" and "field 2"

designations have significance. In many other applications

the field data is not important or may be ignored.

Typically in these cases ":" or ";" are used as default.

Implementers and users should exercise caution regarding

this aspect of time code.

 

--------------

Encoding of Frames as hh:mm:ss:ff

--------------

 

The SMPTE time code numbering schemes, or "count mode(s)",

label frames as hh:mm:ss:ff starting at an arbitrary zero

origin (00:00:00:00) through approximately 24 hours,

depending on the rate and count mode in use.

 

SMPTE time code supports four frame rates:

-  30/1 (30/1 HDTV)

-  30000/1001 second (NTSC and 30000/1001 digital video)

-  25/1 second (PAL and 25Hz digital video)

-  24/1 second (Film and 24p HDTV)

 

(60/1, 60000/1001, 50/1, and 48/1 can be supported by

treating frames in pairs. This is beyond the scope of this

article)

 

Its important to note that the NTSC frame rate is *not*

29.97, as is often stated. The true and correct rate is

30000/1001, which is 29.97002997002997... (a repeating

decimal fraction). Rounded to 2 decimal points it is 29.97

and this how the 30000/1001 rate gets the name "29.97". But

29.97 is not accurate enough for many purposes, especially

maintaining video/audio sync.

 

The "count modulus", that is, the number of unit counts per

time, is referred to as "frames per (nominal) second":

 

30fps (0-29 frames)

25fps (0-24 frames)

24fps (0-23 frames).

 

Here, "nominal" (name of) is used to indicate that a

"second" is not a true second in the case of 30000/1001

rate video.

 

--------------

Non-drop Frame and Drop Frame Count Modes

--------------

 

The "Non-drop Frame" count mode labels frames with an

uninterrupted incrementing count of hh:mm:ss:ff. It is

applicable to all rates. In the case of 30000/1001 (NTSC),

the hh:mm:ss portions of a Non-drop Frame count does not

indicate accurate running time because the frame rate is

not exactly "30 frames per second".

 

The "Drop Frame" count mode was developed to compensate for

the slightly slow frame rate of NTSC. 30000/1001 is

approximately 29.97002997.... , that is, 30 frames last for

slightly longer than true 30 frames-per-second. Put another

way, an "NTSC second" is slightly longer than a true second

- (1 / (30000 / 1001)) * 30 = 1.001 seconds. Thus there is

a "drift" between the true time and uncompensated (Non-drop

Frame) hh:mm:ss:ff representation. To compensate, the Drop

Frame count mode skips, or "drops", certain numbers from

the count. The rule is:

 

"Drop" (omit) the frame numbers 00 and 01 from the count

every minute except each tenth minute; that is, except each

minute that is divisible by 10.

 

To quote SMTPE ST 12-1, section 5.2.2 Drop frame - NTSC

time compensated mode:

To minimize the NTSC time deviation from real time, the

first two frame numbers (00 and 01) shall be omitted from

the count at the start of each minute except minutes 00,

10, 20, 30, 40, and 50.

 

So, at the end of the first minute the count "skips" from

00:00:59;29 to 00:01:00:00;02. The numbers 00:01:00:00;00

and 00:01:00:00;01 are "dropped", or omitted, from the

count. At the end of the tenth minute the count does not

skip - the count goes smoothly from 00:09:59;29 to

00:10:00:00;00 to 00:10:00:00;01.

 

The NTSC video signal is always the same; the count modulus

(30fps, 0-29) and frame rate (30000/1001) do not change

whether Non-drop or Drop-frame count mode is used. Drop

Frame is only a numbering scheme - actual video frames are

not dropped! Where would they go?

 

--------------

Drop Frame Accuracy and Broadcast "Daily Jam"

--------------

 

The Drop Frame count mode does a good job of distributing

the drift, or error, between true "clock time" and the

30000/1001 frame rate to produce usable "time-of-day"

hh:mm:ss:ff values. But it is not exactly accurate; there

is approximately a -2.6 frame error at the end of the 24-

hour day.

 

The Drop Frame value 24:00:00;00 = 2589408 frames, but 24 *

60 * 60 * (30000/1001) = 2589410.589410594. frames. The

Drop Frame count mode under-counts by approximately

-2.6 frames: 2589408 - 2589410.589410594. = -2.589410594.

frames. (There's that repeating decimal fraction again,

this time in the total frames in 24 hours, an unavoidable

consequence of the 30000/1001 rate no matter how you cut

it).

 

In production and post-production this inaccuracy can

generally be ignored, but in broadcast synchronization it

is traditional to reset the time code value once a day to

keep the indicated time-of-day as close as possible to true

clock time. This is sometimes called "daily jam", where the

time code value is "jammed", or reset, to an accurate

clock. Thus, time code values are very nearly the true

time-of-day and the error does not accumulate each day.

 

 

//////////////////////////////////////////////////////////////////////

// SMPTETimecode.h

 

#include <stdio.h>

#include <stdlib.h> // for atol()

 

typedef enum SMPTETCBASERATE

{

SMPTETCBASERATE_NOT_SET  = 0,

SMPTETCBASERATE_30FPS    = 1,  // NTSC

SMPTETCBASERATE_25FPS    = 2,  // PAL

SMPTETCBASERATE_24FPS    = 3,  // Film or 24p

} SMPTETCBASERATE;

 

typedef enum SMPTETCTYPE

{

SMPTETCTYPE_NOT_SET                = 0,

SMPTETCTYPE_FIELD1_NON_DROP_FRAME = 1, // '.'  period

SMPTETCTYPE_FIELD2_NON_DROP_FRAME = 2, // ':'  colon

SMPTETCTYPE_FIELD1_DROP_FRAME      = 3, // ','  comma

SMPTETCTYPE_FIELD2_DROP_FRAME      = 4, // ';'  semi colon

} SMPTETCTYPE;

 

// convert hh:mm:ss:ff to frames

int SMPTE_HMSF_To_Frames(  long lHr,

                          long lMn,

                          long lSc,

                          long lFr,

                          SMPTETCBASERATE SMPTETCBASERATEx,

                          SMPTETCTYPE SMPTETCTYPEx,

                          long* plFramesResult);

 

// convert timecode character representation to frames

int SMPTE_TextTC_To_Frames( char* sTimecode,

                            SMPTETCBASERATE SMPTETCBASERATEx,

                            long* plFramesResult);

 

// convert frames to hh MM ss ff

int SMPTE_Frames_To_HMSF(  long lFrames,

                          SMPTETCBASERATE SMPTETCBASERATEx,

                          SMPTETCTYPE SMPTETCTYPEx,

                          long* plHrResult,

                          long* plMnResult,

                          long* plScResult,

                          long* plFrResult);

 

// convert frames to timecode character representation

int SMPTE_Frames_To_TextTC(  long lFrames,

                            SMPTETCBASERATE SMPTETCBASERATEx,

                            SMPTETCTYPE SMPTETCTYPEx,

                            char* sTimecodeResult);

//////////////////////////////////////////////////////////////////////

 

*/////////////////////////////////////////////////////////////////////

#endif // #ifdef SMPTE_TIMECODE_INTRODUCTION

 

#include "SMPTETimecode.h"

 

 

int SMPTE_HMSF_To_Frames(  long lHr,

                          long lMn,

                          long lSc,

                          long lFr,

                          SMPTETCBASERATE SMPTETCBASERATEx,

                          SMPTETCTYPE SMPTETCTYPEx,

                          long* plFramesResult)

{

 

///////////////////////////////////////////////////////

// convert hh:mm:ss:ff to frames

 

// params -

//

// long lHours - input hours

// long lMins - input minutes

// long lSecs - input seconds

// long lFrames - input frames

 

// SMPTETCBASERATEx one of (from "SMPTETimecode.h") -

//    typedef enum SMPTETCBASERATE

//    {

//    SMPTETCBASERATE_NOT_SET  = 0,  // error

//    SMPTETCBASERATE_30FPS    = 1,  // NTSC

//    SMPTETCBASERATE_25FPS    = 2,  // PAL

//    SMPTETCBASERATE_24FPS    = 3,  // Film or 24p

//    } SMPTETCBASERATE;

//

// SMPTETCTYPEx one of (from "SMPTETimecode.h")

//    typedef enum SMPTETCTYPE

//    {

//    SMPTETCTYPE_NOT_SET                = 0,

//    SMPTETCTYPE_FIELD1_NON_DROP_FRAME = 1, // '.'  period

//    SMPTETCTYPE_FIELD2_NON_DROP_FRAME = 2, // ':'  colon

//    SMPTETCTYPE_FIELD1_DROP_FRAME      = 3, // ','  comma

//    SMPTETCTYPE_FIELD2_DROP_FRAME      = 4, // ';'  semi colon

//    } SMPTETCTYPE;

//

// plFramesResult - pointer to caller supplied long varible to receive result

//

// Return -

// Returns 0;  // no error handling provided

//

///////////////////////////////////////////////////////

 

int iRet = 0; // no error handling provided

 

long lFramesCalc = 0;

 

if(SMPTETCBASERATEx == SMPTETCBASERATE_30FPS)

  {

  if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME

      || SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)

    {

    // convert as 30 fps non-drop frame

 

    lFramesCalc =  lHr  * 108000;    // 30 * 60 * 60 non-drop frames in 1 hour

    lFramesCalc += lMn  * 1800;      // 30 * 60 non-drop frames in one minute 

    lFramesCalc += lSc  * 30;        // 30 non-drop frames in one second

    lFramesCalc += lFr;              // frames

 

    }

  else if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_DROP_FRAME

      || SMPTETCTYPEx == SMPTETCTYPE_FIELD2_DROP_FRAME)

    {

    // convert as 30 fps drop-frame

 

    lFramesCalc =  lHr  * 107892;    // ((30 * 60 - 2) * 10 + 2) * 6 drop-frame frames in 1 hour

    lFramesCalc += lMn  * 1798;      // 30 * 60 - 2 drop-frame frames in one minute

    lFramesCalc += (lMn / 10) * 2;  // for each minute except each 10th minute add 2 frames

    lFramesCalc += lSc  * 30;        // 30 drop-frame frames in one second

    lFramesCalc += lFr;              // frames

 

    }

  }

else if(SMPTETCBASERATEx == SMPTETCBASERATE_25FPS)

  {

  if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME

      || SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)

    {

    // convert as 25 fps non-drop frame

 

    lFramesCalc =  lHr  * 90000;    // 25 * 60 * 60 non-drop frames in 1 hour

    lFramesCalc += lMn  * 1500;      // 25 * 60 non-drop frames in one minute 

    lFramesCalc += lSc  * 25;        // 25 non-drop frames in one second

    lFramesCalc += lFr;              // frames

 

    }

  else

    {

    // drop-frame couting mode is not supported for 25 fps

    }

  }

else if(SMPTETCBASERATEx == SMPTETCBASERATE_24FPS)

  {

  if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME

      || SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)

    {

    // convert as 24 fps non-drop frame

 

    lFramesCalc =  lHr  * 86400;    // 24 * 60 * 60 non-drop frames in 1 hour

    lFramesCalc += lMn  * 1440;      // 24 * 60 non-drop frames in one minute

    lFramesCalc += lSc  * 24;        // 24 non-drop frames in one second

    lFramesCalc += lFr;              // frames

 

    }

  else

    {

    // drop-frame couting mode is not supported for 24 fps

    }

  }

 

*plFramesResult = lFramesCalc;

 

return iRet;

 

} // SMPTE_TextTC_To_Frames()

 

int SMPTE_TextTC_To_Frames(  char* sTimecode,

                            SMPTETCBASERATE SMPTETCBASERATEx,

                            long* plFramesResult)

{

 

///////////////////////////////////////////////////////

// convert timecode character representation to frames

 

// params -

//

// sTimecode - input timecode character string (null terminated char string)

 

// SMPTETCBASERATEx one of (from "SMPTETimecode.h") -

//    typedef enum SMPTETCBASERATE

//    {

//    SMPTETCBASERATE_NOT_SET  = 0,  // error

//    SMPTETCBASERATE_30FPS    = 1,  // NTSC

//    SMPTETCBASERATE_25FPS    = 2,  // PAL

//    SMPTETCBASERATE_24FPS    = 3,  // Film or 24p

//    } SMPTETCBASERATE;

 

// plFramesResult - pointer to called supplied long varible to receive result

// Return -

// Returns 0;  // no error handling provided

//

///////////////////////////////////////////////////////

 

int iRet = 0; // no error handling provided

 

 

// parse the timecode string to obtain hh:mm:ss:ff values as integers

// atol() stops reading the string at any non-numeric character

// so the separators in 00:00:00:00 conveniently terminate each field

long lHours = atol(sTimecode + 0);    // xx:00:00:00

long lMins = atol(sTimecode + 3);    // 00:xx:00:00

long lSecs = atol(sTimecode + 6);    // 00:00:xx:00

long lFrames = atol(sTimecode + 9);  // 00:00:00:xx

 

// get the SMPTETCTYPE from the string

SMPTETCTYPE SMPTETCTYPEx = SMPTETCTYPE_NOT_SET;

char cTcType = sTimecode[8]; // last separator

if(cTcType == '.')      // period

 

SMPTETCTYPEx = SMPTETCTYPE_FIELD1_NON_DROP_FRAME;

else if(cTcType == ':')  // colon

  SMPTETCTYPEx = SMPTETCTYPE_FIELD2_NON_DROP_FRAME;

else if(cTcType == ',')  // comma

  SMPTETCTYPEx = SMPTETCTYPE_FIELD1_DROP_FRAME;

else if(cTcType == ';')  // semicolon

  SMPTETCTYPEx = SMPTETCTYPE_FIELD2_DROP_FRAME;

 

long lFramesResult = 0;

 

SMPTE_HMSF_To_Frames(  lHours,

                      lMins,

                      lSecs,

                      lFrames,

                      SMPTETCBASERATEx,

                      SMPTETCTYPEx,

                      &lFramesResult);

 

*plFramesResult = lFramesResult;

 

return iRet;

 

} // SMPTE_TextTC_To_Frames()

 

 

int SMPTE_Frames_To_HMSF(  long lFrames,

                          SMPTETCBASERATE SMPTETCBASERATEx,

                          SMPTETCTYPE SMPTETCTYPEx,

                          long* plHrResult,

                          long* plMnResult,

                          long* plScResult,

                          long* plFrResult)

{

///////////////////////////////////////////////////////

// convert frames to hh mm ss ff

//

// params -

// lFrames - input frames value

//

// SMPTETCBASERATEx one of (from "SMPTETimecode.h")

//    typedef enum SMPTETCBASERATE

//    {

//    SMPTETCBASERATE_NOT_SET  = 0,  // error

//    SMPTETCBASERATE_30FPS    = 1,  // NTSC

//    SMPTETCBASERATE_25FPS    = 2,  // PAL

//    SMPTETCBASERATE_24FPS    = 3,  // Film or 24p

//    } SMPTETCBASERATE;

//

// SMPTETCTYPEx one of (from "SMPTETimecode.h")

//    typedef enum SMPTETCTYPE

//    {

//    SMPTETCTYPE_NOT_SET                = 0,

//    SMPTETCTYPE_FIELD1_NON_DROP_FRAME = 1, // '.'  period

//    SMPTETCTYPE_FIELD2_NON_DROP_FRAME = 2, // ':'  colon

//    SMPTETCTYPE_FIELD1_DROP_FRAME      = 3, // ','  comma

//    SMPTETCTYPE_FIELD2_DROP_FRAME      = 4, // ';'  semi colon

//    } SMPTETCTYPE;

//

// long* plHrResult - pointer to caller supplied long to receive hours result

// long* plMnResult - pointer to caller supplied long to receive minutes result

// long* plScResult - pointer to caller supplied long to receive seconds result

// long* plFrResult - pointer to caller supplied long to receive frames result

//

// Return -

// Returns 0;  // no error handling provided

//

///////////////////////////////////////////////////////

 

int iRet = 0; // no error handling provided

 

long lHr = 0;

long lMn = 0;

long lSc = 0;

long lFr = 0;

 

long lQuot = 0;

long lRem = 0;

 

 

if(SMPTETCBASERATEx == SMPTETCBASERATE_30FPS)

  {

  if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME

      || SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)

    {

    // convert as 30 fps non-drop frame

 

    #ifdef USE_24HOUR_ROLLOVER

    if(lFrames >= 2592000)     // 24 hour rollover

      lFrames -= 2592000;

    #endif // #ifdef USE_24HOUR_ROLLOVER

 

    // HOURS /////////////////

    lQuot = lFrames / 108000;  // frames in non-drop-frame hour

    lRem = lFrames % 108000;

    lHr = lQuot;

 

    // MINUTES /////////////////

    lQuot = lRem / 1800;      // frames in non-drop-frame minute

    lRem = lRem % 1800;

    lMn = lQuot;

 

    // SECONDS //////////////

    lQuot = lRem / 30;        // frames in non-drop-frame second

    lRem = lRem % 30;

    lSc = lQuot;

 

    // FRAMES ///////////////

    lFr = lRem;

 

    // return results

    *plHrResult = lHr;

    *plMnResult = lMn;

    *plScResult = lSc;

    *plFrResult = lFr;

 

    }

  else if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_DROP_FRAME

      || SMPTETCTYPEx == SMPTETCTYPE_FIELD2_DROP_FRAME)

    {

    // convert as 30 fps drop-frame

 

    #ifdef USE_24HOUR_ROLLOVER

    if(lFrames >= 2589408)     // 24 hour rollover

      lFrames -= 2589408;

    #endif // #ifdef USE_24HOUR_ROLLOVER

 

    long lCross10x2 = 0;  // crossed 10 minutes

    long lEven10 = 0;    // is even 10 minutes

 

    // HOURS ///////////////////

    lQuot = lFrames / 107892;  // frames in drop-frame hour

    lRem = lFrames % 107892;

    lHr = lQuot;

 

    // MINUTES /////////////////

    lQuot = lRem / 1798;      // frames in drop-frame minute

    lRem = lRem % 1798;

    lCross10x2 = (lQuot / 10) * 2;  // calc two frames for each 10 minute crossing

    lEven10 = lQuot % 10;            // calc is even 10th minute

 

    if(lRem < (lCross10x2) && lEven10 == 0)

      {

      lQuot -= 1;

      lRem += (1798 + 2) - (lCross10x2);

      }

    else

      {

      if(lRem < ((lCross10x2) + 2) && lEven10 != 0)

        {

        lQuot -= 1;

        lRem += 1798 - (lCross10x2);

        }

      else

        {

        //lQuot = lQuot;

        lRem -= (lCross10x2);

        }

      }

    lMn = lQuot;  // minute

 

    // SECONDS //////////////

    lQuot = lRem / 30;

    lRem = lRem % 30;

    lSc = lQuot;

 

    // FRAMES ///////////////

    lFr = lRem;

 

    // return results

    *plHrResult = lHr;

    *plMnResult = lMn;

    *plScResult = lSc;

    *plFrResult = lFr;

 

    }

  }

else if(SMPTETCBASERATEx == SMPTETCBASERATE_25FPS)

  {

  if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME

      || SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)

    {

    // convert as 25 fps non-drop frame

 

    #ifdef USE_24HOUR_ROLLOVER

    if(lFrames >= 2160000)     // 24 hour rollover

      lFrames -= 2160000;

    #endif // #ifdef USE_24HOUR_ROLLOVER

 

    // HOURS /////////////////

    lQuot = lFrames / 90000;  // frames in non-drop-frame hour

    lRem = lFrames % 90000;

    lHr = lQuot;

 

    // MINUTES /////////////////

    lQuot = lRem / 1500;      // frames in non-drop-frame minute

    lRem = lRem % 1500;

    lMn = lQuot;

 

    // SECONDS //////////////

    lQuot = lRem / 25;        // frames in non-drop-frame second

    lRem = lRem % 25;

    lSc = lQuot;

 

    // FRAMES ///////////////

    lFr = lRem;

 

    // return results

    *plHrResult = lHr;

    *plMnResult = lMn;

    *plScResult = lSc;

    *plFrResult = lFr;

 

    }

  else

    {

    // drop-frame count mode is not supported for 25 fps

    }

  }

else if(SMPTETCBASERATEx == SMPTETCBASERATE_24FPS)

  {

  if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME

      || SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)

    {

    // convert as 24 fps non-drop frame

 

    #ifdef USE_24HOUR_ROLLOVER

    if(lFrames >= 2073600)     // 24 hour rollover

      lFrames -= 2073600;

    #endif // #ifdef USE_24HOUR_ROLLOVER

 

    // HOURS /////////////////

    lQuot = lFrames / 86400;  // frames in non-drop-frame hour

    lRem = lFrames % 86400;

    lHr = lQuot;

 

    // MINUTES /////////////////

    lQuot = lRem / 1440;      // frames in non-drop-frame minute

    lRem = lRem % 1440;

    lMn = lQuot;

 

    // SECONDS //////////////

    lQuot = lRem / 24;        // frames in non-drop-frame second

    lRem = lRem % 24;

    lSc = lQuot;

 

    // FRAMES ///////////////

    lFr = lRem;

 

    // return results

    *plHrResult = lHr;

    *plMnResult = lMn;

    *plScResult = lSc;

    *plFrResult = lFr;

 

    }

  else

    {

    // drop-frame count mode is not supported for 24 fps

    }

  }

 

return iRet;

 

} // SMPTE_Frames_To_HMSF()

 

int SMPTE_Frames_To_TextTC(  long lFrames,

                            SMPTETCBASERATE SMPTETCBASERATEx,

                            SMPTETCTYPE SMPTETCTYPEx,

                            char* sTimecodeResult)

{

///////////////////////////////////////////////////////

// convert frames to timecode character representation

 

// params -

// lFrames - input frames value

//

// SMPTETCBASERATEx one of (from "SMPTETimecode.h")

//    typedef enum SMPTETCBASERATE

//    {

//    SMPTETCBASERATE_NOT_SET  = 0,  // error

//    SMPTETCBASERATE_30FPS    = 1,  // NTSC

//    SMPTETCBASERATE_25FPS    = 2,  // PAL

//    SMPTETCBASERATE_24FPS    = 3,  // Film or 24p

//    } SMPTETCBASERATE;

//

// SMPTETCTYPEx one of (from "SMPTETimecode.h")

//    typedef enum SMPTETCTYPE

//    {

//    SMPTETCTYPE_NOT_SET                = 0,

//    SMPTETCTYPE_FIELD1_NON_DROP_FRAME = 1, // '.'  period

//    SMPTETCTYPE_FIELD2_NON_DROP_FRAME = 2, // ':'  colon

//    SMPTETCTYPE_FIELD1_DROP_FRAME      = 3, // ','  comma

//    SMPTETCTYPE_FIELD2_DROP_FRAME      = 4, // ';'  semi colon

//    } SMPTETCTYPE;

//

// sTimecodeResult - output timecode character string (null terminated char string)

//

// Return -

// Returns 0;  // no error handling provided

//

///////////////////////////////////////////////////////

 

int iRet = 0; // no error handling provided

 

long lHr = 0;

long lMn = 0;

long lSc = 0;

long lFr = 0;

 

SMPTE_Frames_To_HMSF(  lFrames,

                      SMPTETCBASERATEx,

                      SMPTETCTYPEx,

                      &lHr,

                      &lMn,

                      &lSc,

                      &lFr);

 

// HMSF to ascii text /////////////

// "00:00:00:00"

sprintf(sTimecodeResult, "%02d:%02d:%02d:%02d",

lHr,

lMn,

lSc,

lFr

);

 

// Set the last separator to timecode type character

if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME)

  sTimecodeResult[8] = '.';  // period

else if(SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)

  sTimecodeResult[8] = ':';  // colon

else if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_DROP_FRAME)

  sTimecodeResult[8] = ',';  // comma

else if(SMPTETCTYPEx == SMPTETCTYPE_FIELD2_DROP_FRAME)

  sTimecodeResult[8] = ';';  // semicolon

 

return iRet;

 

} // SMPTE_Frames_To_TextTC()